What could be causing my document element to suddenly shift to the side of the page with just a slight movement?

I'm facing an issue with dragging an element just a bit on a mousedown event to reveal a new item underneath. However, whenever I try to move it, the element jumps all the way to the right side of the window instead of where I intended it to go. I have experimented with changing positions from 'relative' to 'absolute', setting fixed x and y positions, and making them relative to the mouse event, but the problem persists.

It seems like there's something fundamental that I am missing here. In my background, I've heavily relied on vue.js to handle DOM manipulation, as I don't have much experience in this area. This challenge arose in a Vue project initially, leading me to believe it was a Vue-related issue, which was later proven not to be the case.

document.getElementById("red").addEventListener("mousedown", mouseDown);
function mouseDown(e) {

  const el = e.target;
  // const x = e.pageX;
  // const y = e.pageY;

  const rect = el.getBoundingClientRect();
  const x = rect.left + 20;
  const y = rect.top + 20;

  el.style.left = `${x}px`
  el.style.top = `${y}px`

  const newrect = el.getBoundingClientRect();

  document.getElementById("from").innerHTML = "drag from:      " + rect.left + ", " + rect.top;
  document.getElementById("to").innerHTML = "try to drag to: " + x + ", " + y;
  document.getElementById("result").innerHTML = "dragged to:     " + + newrect.left + ", " + newrect.top;
}
.box {
  position:relative;
  margin: auto;
  border: 3px solid #73AD21;
  width: 200px;
  height: 250px;
}
.button {
  position:relative;
  background-color: red;
  color: white;
  padding: 10px 20px;
  text-align: center;
  width: 100px;
  font-size: 16px;
  cursor: pointer;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Drag Drop Test</title>
</head>
   
<body>
<div id="app" align="center" class="box">
<button id="red" class="button" >Red</button>
<p id="from"></p>
<p id="to"></p>
<p id="result"></p>
</div>
</body>
</html>

Answer №1

The button is being positioned to the far right due to the code snippet below:

const el = e.target; //el refers to the button with id "red" that you clicked on

const rect = el.getBoundingClientRect(); //this function returns the size of the button and its current position relative to the viewport (not the parent element)

const x = rect.left + 20;
const y = rect.top + 20;

el.style.left = `${x}px`; //moves the button by 20px along the x-coordinate.
el.style.top = `${y}px`; //moves the button by 20px along the y-coordinate.

Since both the button and the box are set to have a relative position in your CSS, when you move the button 20px from the left and top, it moves all the way to the extreme right. To keep the button inside the box, you need to calculate the positions relative to the parent element (the box) rather than the entire viewport. Additionally, the position of the button should be set to absolute since the position of the box is relative.

Below is the modified code that ensures the button stays draggable inside the box only. I hope this solution addresses your issue.

// JavaScript code
const button = document.getElementById("red");
const box = document.getElementById("app");

const boxWidth = box.offsetWidth + 3;
const boxHeight = box.offsetHeight + 3; // including border

const buttonWidth = button.offsetWidth;
const buttonHeight = button.offsetHeight;

let dragStartX, dragStartY; // drag start positions
let buttonX, buttonY;

let isDragging; // check if currently dragging or not
let marginReached = false; // flag to determine if button reaches the box's margin

button.addEventListener("mousedown", handleMouseDown);
button.addEventListener("mousemove", handleMouseMove);
button.addEventListener("mouseup", handleMouseUp);

// handle mousedown events
function handleMouseDown(e) {
    const boxRect = box.getBoundingClientRect();
    const buttonRect = button.getBoundingClientRect();
    const LEFT = buttonRect.left - boxRect.left;
    const TOP = buttonRect.top - boxRect.top;
    isDragging = true; // set dragging to true
    dragStartX = e.clientX; // mouse pointer position on the x-axis
    dragStartY = e.clientY; // mouse pointer position on the y-axis
    buttonX = LEFT; // position of the button on the x-axis
    buttonY = TOP; // position of the button on the y-axis
}

// handle mousemove events
function handleMouseMove(e) {
    const mouseX = e.clientX; // current mouse position on the x-axis
    const mouseY = e.clientY; // current mouse position on the y-axis

    const moveFromLeft = mouseX - dragStartX + buttonX;
    const moveFromTop = mouseY - dragStartY + buttonY;

    // prevent the button from moving past the margins of the box
    if (
        moveFromLeft >= boxWidth - buttonWidth ||
        moveFromTop >= boxHeight - buttonHeight ||
        moveFromLeft <= 0 ||
        moveFromTop <= 0
    ) {
        marginReached = true;
    } else {
        marginReached = false;
    }

    if (isDragging && !marginReached) {
        button.style.left = `${moveFromLeft}px`;
        button.style.top = `${moveFromTop}px`;
    }
}

// handle mouseup events
function handleMouseUp(e) {
    isDragging = false; // set dragging to false on mouse button release
}
.box {
    position: relative;
    margin: auto;
    border: 3px solid #73AD21;
    width: 200px;
    height: 250px;
}
.button {
    position: absolute;
    left: 25%;
    background-color: red;
    color: white;
    padding: 10px 20px;
    text-align: center;
    width: 100px;
    font-size: 16px;
    cursor: pointer;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Drag Drop Test</title>
  </head>
   
  <body>
    <div id="app" align="center" class="box">
      <button id="red" class="button" >Red</button>
      <p id="from"></p>
      <p id="to"></p>
      <p id="result"></p>
    </div>
  </body>
</html>

Answer №2

Breaking down @Mol nAK's response to fit my unique needs, I am looking for the following solution:

  const redButton = document.getElementById("red");
  redButton.addEventListener("mousedown", handleMouseDown);

  function handleMouseDown(event) {
    const parentRectangle = redButton.parentElement.getBoundingClientRect();
    const buttonRectangle = redButton.getBoundingClientRect();

    const distanceFromLeft = buttonRectangle.left - parentRectangle.left;
    const distanceFromTop =  buttonRectangle.top - parentRectangle.top; 
  
    redButton.style.left = `${distanceFromLeft}px`
    redButton.style.top = `${distanceFromTop}px`
  }

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

Adding dropdown values to text area

I'm encountering a simple issue. My goal is to allow users to select values from a dropdown menu and have those values added to a text area. Additionally, users should be able to input extra content in the text area. Here's what I want: the user ...

Determining the checkbox value in a Django template based on views

I have been working on designing a Django page that functions in the following way. On my "list_books.html" page, all book objects are displayed that are provided to it. I have several functions in views.py that determine which values will be used to dis ...

Utilizing the `theme` property in makeStyles with Material-UI

I am currently working on passing down an muiTheme to a component through the use of ThemeProvider, and I also want to apply it to the children components. Furthermore, I aim to utilize the theme's properties in both components by creating a classes o ...

Styling elements conditionally with AngularJS-controlled CSS

Looking for some guidance in Angular as a newcomer. I am attempting to adjust a style when clicked. On my page, I have multiple content spaces created using the same template. Upon clicking a "more" link, I desire to expand that specific section. I have ac ...

CSS3's auto property for margin

This is a snippet of my HTML and CSS code that I'm working on: <header id="main"> <a href="#" id="exit"></a> <a href="http://www.reddit.com/" id="reddit"></a> <a href="http://www.stackoverflow.com/" id="st ...

How can I resize an element using jQuery resizable and then revert it back to its original size with a button click?

I need help figuring out how to revert an element back to its original size after it has been modified with .resizable. I attempted the following: <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="//code. ...

What is the process for adding a button and redirecting to a different view in MVC4?

I am facing an issue that I need help with. In my application, there is a view called CustomerMaster. This view includes fields such as 'Area', 'City', and more. If the city is already in the list, it automatically appears in a dropdow ...

Personalize Autocomplete CSS based on the TextField input in React Material UI when a value is present

In my current project, I am utilizing React Material Autocomplete fields, which includes a nested TextField. I have successfully implemented standard styles for when the field is empty and only the label is visible, as well as different styles for hover ef ...

ES6: utilizing properties and methods of a subclass that inherit from the superclass

While ES6 does not have abstract methods or properties, is it possible to access some methods or properties from the parent class in an inherited class? class ParentClass { constructor(){ ParentClass.checkChildPropertyAccessibility(); Pa ...

Nashorn encountered an issue: java.lang.NoSuchMethodException was raised due to the absence of the renderServer function

Encountering unusual behavior with Nashorn and React-redux. Initially, a straightforward JavaScript code is babelified before deployment. Here's the JSX snippet from the resultant babelified file: var React = require('react'); var ReactDOM ...

NEXT.JS - LocalStorage unexpectedly resets data to its initial state instead of persisting changes after the page is refreshed

Upon initial component run, "1" is displayed. Clicking the button appends it by 3 successfully. The value inside local storage also updates accordingly. However, upon reloading the page, the local storage reverts back to 1. What could be the missing piec ...

What is the best way to design a navigation bar for a one-page application using Vue?

Currently, I am developing a Vuejs single-page application and I'm exploring ways to implement a navbar that can toggle the visibility of different sections within the app upon clicking. While I have successfully designed the navbar layout, I am encou ...

At times, MomentJS may miscalculate and add an incorrect number of hours

My goal is to add a specific amount of hours to a 24-hour time, but I'm encountering an issue with the time 00:00. The code I've written works correctly for all times except midnight. For example, if I have 01:30 and add 1 hour, it gives me 02:3 ...

Incorporate JSON data to display SVG images

As a beginner in web development, I have been honing my skills with the AngularJS framework due to its user-friendly nature. Currently, I'm working on pulling JSON data from an API using $http.get method. One of the fields contains an image source i ...

Why can't I access my PHP variable in an external JavaScript file?

I'm experiencing a problem with integrating an external JS file and Google Maps. The issue lies in the fact that the external JS file is not recognizing an array I have created within the PHP file. This setup is within a WordPress environment. Specifi ...

Discovering and revising an item, delivering the complete object, in a recursive manner

After delving into recursion, I find myself at a crossroads where I struggle to return the entire object after making an update. Imagine you have an object with nested arrays containing keys specifying where you want to perform a value update... const tes ...

Should I include JSX or JS when exporting ReactJS components as node modules to npm?

I've been busy developing React.js components and sharing them as modules on npm. My approach involves utilizing a gulp task to convert all jsx components into js, leveraging gulp-react: var react = require('gulp-react'); gulp.task(' ...

Filtering a list with Vue.js using an array of checkboxes

Currently, I am exploring ways to filter a v-for list using a checkbox model array with multiple checkboxes selected. Most examples I have come across only demonstrate filtering one checkbox at a time. In the demo here, you can see checkboxes 1-3 and a lis ...

Combine the given name with the initial of their surname

Recently, I've been working with the Chakra UI Avatar component and ran into an issue. When attempting to display both first and last name initials, only the first name initial is showing. This problem arises when trying use user data retrieved from a ...

The method .makePerspective() in THREE.Matrix4 has been updated with a new signature. Make sure to refer to the documentation for more information

Attempting to run a functional three.js code using release 119 of three.js (instead of r79) has resulted in an error being thrown by the previously functioning code: THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check ...