The offset values of $(element) keep increasing indefinitely when they are updated repeatedly

After researching how to utilize JavaScript drag functionality to move elements, the goal is to dynamically adjust the cursor position within a square when dragged. In an attempt to simplify the process and avoid storing x and y offsets as separate variables for future use, I have modified the code snippet by utilizing $('.target').offset() to obtain the top and left offset values.

 (function () {
    let x = 0;
    let y = 0;
    let target = document.querySelector('.target');
    let mouseDown = false;
    target.addEventListener(
        'mousedown',
        function (e) {
            mouseDown = true;
            target.style.position = 'relative';
            x = target.offsetLeft - e.clientX;
            y = target.offsetTop - e.clientY;
        }
    );
    document.addEventListener(
        'mouseup',
        function () {
            mouseDown = false;
        }
    );

    document.addEventListener(
        'mousemove',
        function (e) {
            event.preventDefault();
            const offset = $(target).offset();
            const offsetLeft = offset.left;
            const offsetTop = offset.top;
            if (mouseDown) {
                console.log(
                    e.clientX + x,
                    offsetLeft,
                    e.clientY + y,
                    offsetTop
                );
                target.style.left = e.clientX + x + 'px';
                target.style.top = e.clientY + y + 'px';
                // target.style.left = offsetLeft + 'px'; // comment out for issue
                // target.style.top = offsetTop + 'px';
            }
        }
    );
})();
 

.target {
    width: 100px;
    height: 100px;
    background-color: #0000FF;
}
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<div class="target"></div>

The above code shows that the console displays a comparison between the suggested solution coordinates and their respective offsets as the square is being dragged:

61 69 19 27
62 69 19 27
62 70 19 27
63 70 19 27
63 71 19 27
64 71 19 27
    ...

These values should work correctly when assigned. However, substituting the lines below:

target.style.left = e.clientX + x + 'px';
target.style.top = e.clientY + y + 'px';

with the commented-out lines causes issues with the offsets calculated using $('.target').offsets():

// target.style.left = offsetLeft + 'px';
// target.style.top = offsetTop + 'px';

The problem arises as the offset calculations with $('.target').offsets() continue to increase exponentially:

212 2408 424 2408
172 2416 423 2416
146 2424 418 2424
133 2432 409 2432
127 2440 401 2440
       ...

There seems to be an oversight causing this issue. Why does it only occur when using the offset values?

Answer №1

The default margin for the body of an html element in Chrome is set to 8px. In a simple scenario where there is only one element in the body, and the target has position: relative;, with its left and top properties defined in pixels, the calculation shows that $(target).offset().top amounts to target.style.top plus 8px, and $(target).offset().left equals to target.style.left plus 8px.

The value of 8 can be computed programmatically by taking the difference between

$(target).offset().left - $('html').offset().left
and
$(target).offset().top - $('html').offset().top
.

To confirm the consistent 8px offset, you can replace the console.log(...) within your mousemove handler with

console.log(offsetTop - parseFloat(target.style.top) , offsetTop - parseFloat(target.style.left))
and uncomment the other section.

Therefore, upon executing

target.style.left = $(target).offset().left + 'px'
, the resulting output will show "previous value + 8px". The same applies to the .top property. Each time the mousemove function is triggered, the top/left values increase by 8px. With frequent calls to mousemove in a short time frame, this cumulative effect becomes noticeable quickly.

If additional elements are present, a similar phenomenon occurs where the increment continues indefinitely after each reassignment (although the incremental change per handler call may vary from the initial 8) unless the change amount is precisely 0.


I am uncertain if there exists a straightforward solution to address your issue. However, given your desire to drag the item, I could craft a basic code snippet for accomplishing that task. One key suggestion is to persist certain data during mousedown events, and subsequently calculate the new position based on the previous data during mousemove operations, to prevent inadvertent accumulation of x/y values. In this context, I utilize the expression x = oldX + ... where oldX signifies the value from mousedown, rather than x = x + ... which could result in rapid accumulation due to utilizing the prior x value from earlier mousemove executions within the right-hand side.

(function () {
    let x = 0;
    let y = 0;
    let oldX = 0;
    let oldY = 0;
    let pressX = 0;
    let pressY = 0;

    let target = document.querySelector('.target');
    let mouseDown = false;
    target.addEventListener(
        'mousedown',
        function (e) {
            mouseDown = true;
            target.style.position = 'relative';
            pressX = e.clientX;
            pressY = e.clientY;
            oldX = x;
            oldY = y;
        }
    );
    document.addEventListener(
        'mouseup',
        function () {
            mouseDown = false;
        }
    );

    document.addEventListener(
        'mousemove',
        function (e) {
            event.preventDefault();
            const offset = $(target).offset();
            const offsetLeft = offset.left;
            const offsetTop = offset.top;
            if (mouseDown) {
               x = oldX + e.clientX - pressX
               y = oldY + e.clientY - pressY 
               target.style.left = x + 'px';
               target.style.top = y + 'px';
            }
        }
    );
})();
.target {
    width: 100px;
    height: 100px;
    background-color: #0000FF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<div class="target"></div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating a rotating wheel using JavaScript that is activated by a key press event

I need some help with implementing a keystroke event in this code so that the spinning wheel can start based on a key press, like the spacebar. Any suggestions on how to achieve this? I have found an event code for keystrokes in JavaScript: document.body. ...

Tips for arranging two columns and two rows on mobile using Bootstrap 5

I'm currently using bootstrap 5, and I need to display 2 columns and 2 rows on my mobile device, but it's only showing 1 row. I have two PSDs for this layout in desktop mode: https://i.sstatic.net/HWG6e.png And for mobile mode: https://i.ssta ...

Listing the dates of the month and comparing them with dates from another month using jQuery

Is there a way to generate a list of dates in a month that includes the dates from other months, similar to how the jquery datepicker with showOtherMonths: true works? I was considering creating a calendar with 7 columns (Sun-Sat) and 6 rows to accommodat ...

Achieving Content Centering in React Material UI: A Guide to Aligning to the Middle of the Page

Currently, the button is displaying in the top left corner. How can I move the button to the center of the page? import {Button} from '@mui/material'; function App() { return ( <Button variant="contained">Hello World</ ...

Errors encountered while running `npm install discord.js`

I am currently facing an issue while trying to install discord.js. Unfortunately, I keep encountering the following errors: npm ERR! cb() never called! npm ERR! This is an error with npm itself. Please report this error at: npm ERR! <https://npm.co ...

In the process of creating my initial discord bot, struggling to incorporate advanced functionalities beyond basic commands

I am currently using discord.js and JavaScript to code. I have created a test bot and followed step-by-step guides meticulously, but the bot only responds to basic "ping pong" commands. Whenever I try to add more complex commands, the bot stops functioning ...

Tips for organizing three flexbox divs in a horizontal row

Within the content div, I have three separate divs that resize when the browser window is adjusted. The blue and red divs should maintain a fixed width The green div should resize to fill any available space on the left I attempted to accomplish this usi ...

The functionality to disable the submit button for unchecked radio buttons is not functioning properly

I am currently working on a form where I need to disable the submit button until all fields are filled out. Everything is functioning properly for other field types, EXCEPT FOR RADIO BUTTONS. Even when we do not select a radio option, the Submit button s ...

What is the most efficient way to handle dependencies and instantiate objects just once in JavaScript?

I am interested in discovering reliable and tested design patterns in Javascript that ensure the loading of dependencies only once, as well as instantiating an object only once within the DOM. Specifically, I have the following scenario: // Block A in th ...

Struggling to place buttons below each row in a Bootstrap DataTable, unfortunately, they are only appearing under a specific column rather than the entire row

I have developed a system for managing event expenses that includes an option for multiple installments of a single payment for each event. My goal is to display these installments when a user hovers over an event in the table, with each row representing a ...

The clearTimeout function in React stateless components does not appear to be functioning properly

I am facing an issue with a component that I developed. In this component, one value (inclVal) needs to be larger than another value (exclVal) if both are entered. To ensure that the function handling this comparison doesn't update immediately when pr ...

A guide on utilizing the index column for multiple tables using just one statement in the datatable js library

I've incorporated the datatable js for managing two tables on a single page. HTML <!-- Table#1 --> <table class="dataTable"> <thead> <tr> <td>#</td> <td>col1</td> </tr> &l ...

What is the process of redefining the toString method for a function?

I am currently tackling a coding challenge that involves chaining functions. While researching possible solutions online, I noticed that many of them involved using function.toString and overriding the toString method of a function to create a chained add ...

Highcharts JS encountered an error: x[(intermediate value)(intermediate value)(intermediate value)] is not a valid constructor

I'm in the process of creating a bar chart by fetching options from an ajax response. However, I encountered an error when passing the object to the highcharts constructor. Uncaught TypeError: x[(intermediate value)(intermediate value)(intermediate v ...

Creating a div overlay triggered by the addition of a child tag

Using the Paypal zoid feature, I have a button that opens an iframe in the parent div when clicked. However, the iframe causes the other contents of the website to shift around, making it look messy. I'm wondering if there is a way to make the parent ...

Array of radio buttons submitted via form

I am currently working on a PHP exam form where each option has a corresponding radio button to mark if it is correct. I need to store this information in a database. For each option, if the radio button is checked, it sends a post value of 'Yes&apos ...

Managing images in JavaScript while conserving memory

Welcome I am embarking on a project to construct a webpage using HTML, CSS, JavaScript (jQuery), and nearly 1000 images. The length of the HTML page is substantial, around 5000px. My vision involves creating a captivating visual experience for users as t ...

Executing a function immediately upon the start of a new month in JavaScript (Node.js) without relying on any external libraries?

Is it possible to automate the process of creating a document in MongoDB using Mongoose every 1st of a new month, ideally as soon as the date changes without relying on third-party libraries like needle or cronjob? Can this be achieved solely with setInter ...

Provide the succeeding line from a CSV document whenever a PHP script is executed

I have implemented highcharts to generate a real-time chart with data that updates dynamically. The chart makes an AJAX call to a PHP file which is responsible for parsing a CSV and returning the output as JSON. Below is my Highcharts/jQuery code: functi ...

Is there an issue with retrieving data from asp.cs through ajax?

Currently, I am in the process of learning asp.net and trying to access server data to populate a textbox with the retrieved information. public partial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) ...