Create an element that can be dragged without the need to specify its position as absolute

I am having an issue where elements I want to drag on a canvas are not appearing next to each other as expected. Instead, they are overlapping:

To make these elements draggable, I am utilizing the dragElement(element) function from https://www.w3schools.com/howto/howto_js_draggable.asp.

//Make the DIV element draggagle:
dragElement(document.getElementById("mydiv"));
dragElement(document.getElementById("mydiv2"));

function dragElement(elmnt) {
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  if (document.getElementById(elmnt.id + "header")) {
    // if present, the header is where you move the DIV from:
    document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  } else {
    // otherwise, move the DIV from anywhere inside the DIV:
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv,
#mydiv2 {
  position: absolute;
  z-index: 9;
  background-color: #f1f1f1;
  text-align: center;
  border: 1px solid #d3d3d3;
}

#mydivheader,
#mydivheader2 {
  padding: 10px;
  cursor: move;
  z-index: 10;
  background-color: #2196F3;
  color: #fff;
}
<div class="container">
  <div id="markers" class="row">
    <div id="mydiv" class="col-sm-10">
      <div id="mydivheader">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
    <div id="mydiv2" class="col-sm-10">
      <div id="mydivheader2">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
  </div>
</div>

Changing the CSS position property to relative resolved the spacing issue, but it caused the elements to become unresponsive to dragging.

In my code snippet, only two elements are shown, but in reality, more elements will be dynamically generated using JavaScript.

Here is my JS code :

//This list will be populated by the user      
var d = ["module1", "module2",.......,"module22"];

for(i in d){
  append_module_name_to_drag_div(d[i]);
  dragElement(document.getElementById(d[i]));  
}

function append_module_name_to_drag_div(module_name){
  var mydiv = document.createElement("div");
  mydiv.id=module_name;
  mydiv.className ='col-sm-10';

  var mydivmarker= document.createElement("div");
  mydivmarker.id=module_name+"header";
  mydivmarker.className ='mydivheader';
  
  var marker_image = new Image();
  marker_image.src = "http://www.clker.com/cliparts/w/O/e/P/x/i/map-marker-hi.png";
  marker_image.height = 45;
  marker_image.width = 35;
  
  mydivmarker.append(marker_image);
  mydiv.innerHTML=module_name;
  mydiv.append(mydivmarker);
  document.getElementById("markers").appendChild(mydiv);
}

Answer №1

Due to the declaration of position: absolute in your CSS for #mydiv, the elements are stacked on top of each other. This property removes the element from the normal document flow, causing it to overlap with other flowing elements as defined by the CSS.

Greg recommended using the CSS translate() function for movement instead of relying on position: absolute with values like top. This approach keeps the elements within the flow, ensuring they remain interactable.

Applying translate() after reflow ensures that adjusting draggable elements does not disrupt the layout.

If you're looking for just the outcome, feel free to skip ahead. For further detail:

Note: W3Schools may have outdated information or provide poor advice. It's advisable to supplement their articles with additional research.

Modern browsers support HTML5, CSS3, and ES6 (JavaScript specification) along with various standards such as:

  • Usage of const/let over var for defining block-scoped variables, aiding in debugging and conveying immutability vs. mutability of references.
  • Utilization of Element.querySelector() to search descendants efficiently.
  • Adoption of addEventListener()/removeEventListener() for managing event listeners effectively.
  • Integration of (lambda) arrow functions as substitutes for traditional functions with non-hoisting behavior.
  • Incorporation of semantic HTML tags to enhance web accessibility and comply with standards.

Additional points:

  • Mastering CSS fundamentals before resorting to libraries can improve your understanding and skills.
  • Familiarizing yourself with all HTML tags benefits comprehension, aids in creating accessible content essential for SEO.
  • Acknowledging initiatives like schema.org by Google, Microsoft, etc., for standardizing data markup is crucial but may require extensive reading.

Refactoring

Removing pre-existing comments to accommodate new explanations.

Preference for single-quote (') in strings over double-quote (") in JavaScript code edits.

Transitioned from var to const or let based on context for better variable scoping.

Embracing addEventListener()/removeEventListener() replacing onevent-listeners for consistency.

Main changes highlighted below:

  • In HTML:
    • Drags can utilize the draggable class.
    • Suitable tag usage includes:
      • Headers should be enclosed in <header>.
      • For draggables containing headers, employ <section>.
  • In CSS:
    • Introduced rules for the draggable class.
    • Added enabling CSS properties when draggable also serves as a <section>.
  • In JS:

Answer №2

The most effective method I have discovered is to individually set the top and left properties for each element. This ensures that each element maintains its position without disrupting the draggable functionality:

const d = ["module1", "module2", "module3", "module4", "module5"];

for(i in d){
    append_module_name_to_drag_div(d[i], top, left);
    dragElement(document.getElementById(d[i]));  

    if(left < 220) {
        left = left + 39;
    } else {
        left = 5;
        top = top + 70; // height of marker is 70
    }
}

To enhance this function, I added two additional parameters to specify the values for top and left positions:

function append_module_name_to_drag_div(module_name, top, left) {
    var mydiv = document.createElement("div");
  
    mydiv.id = module_name;
    mydiv.className = 'col-sm-10';
    
    var mydivmarker = document.createElement("div");
    mydivmarker.id = module_name + "header";
    mydivmarker.className = 'mydivheader';
    var marker_image = new Image();
    marker_image.src = "http://www.clker.com/cliparts/w/O/e/P/x/i/map-marker-hi.png";
    marker_image.height = 45;
    marker_image.width = 35;
    mydivmarker.append(marker_image);

    mydiv.innerHTML = module_name;
    mydiv.append(mydivmarker);

    mydiv.style.left = left.toString() + "px";
    mydiv.style.top = top.toString() + "px";

    document.getElementById("markers").appendChild(mydiv);
    var linebreak = document.createElement("br");
    document.getElementById("markers").appendChild(linebreak);
}

This function is based on a W3Schools example with slight modifications specifically where the element's new position is updated:

function dragElement(elmnt) {
  
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  if (document.getElementById(elmnt.id + "header")) {
    document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  } else {
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();

    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;

    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();

    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;

    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  }

  function closeDragElement() {
    document.onmouseup = null;
    document.onmousemove = null;
  }
}

Answer №3

Unique version of w3schools example

If you want to achieve a similar effect as demonstrated in the linked w3schools example, you can utilize the CSS property position: absolute. However, if you do not specify values for top, left, etc., the default values will be applied: top: 0 and left: 0. This will result in the elements stacking on top of each other due to them all appearing at the same location.

To prevent this stacking behavior, you need to assign different values for top, left, etc. to each absolutely positioned element. For instance:

#mydiv2 {
  top: 170px;
}

This solution addresses the scenario with two manually draggable elements, but may not be ideal for dynamically added content.

Functional demonstration:

To simplify the implementation, I omitted the initial if ... else block within the dragElement() function and directly attached the event listener to the entire element:

elmnt.onmousedown = dragMouseDown;

//Enable dragging for the DIV element:
dragElement(document.getElementById("mydiv2"));
dragElement(document.getElementById("mydiv"));

function dragElement(elmnt) {
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // Obtain the initial mouse cursor position:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // Call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // Calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // Set the new position for the element:
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  }

  function closeDragElement() {
    // Stop moving when the mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv, 
#mydiv2 {
  position :absolute;
  z-index: 9;
  background-color: #f1f1f1;
  text-align: center;
  cursor: move;
  border: 1px solid #d3d3d3;
}

#mydiv2 {
  top: 170px;
}

#mydivheader, 
#mydivheader2 {
  padding: 10px;
  z-index: 10;
  background-color: #2196F3;
  color: #fff;
}
<div class="container">
<div class="row">
  <div id="mydiv" class="col-sm-10">
    <div id="mydivheader">Click here to move</div>
    <p>Move</p>
    <p>this</p>
    <p>DIV</p>
  </div>
  <div id="mydiv2" class="col-sm-10">
    <div id="mydivheader2">Click here to move</div>
    <p>Move</p>
    <p>this</p>
    <p>DIV</p>
  </div>
</div>
</div>


Applying transform: translate()

Basic setup

To handle dynamically generated content or cases where explicit positioning properties are not desired, consider utilizing transform: translate() while omitting position: absolute. This approach ensures that elements retain their positions without overlapping.

To implement this, adjust the existing calculation by subtracting the original values from the old translate values before updating them with this difference:

pos1 = pos1 - (pos3 - e.clientX);
pos2 = pos2 - (pos4 - e.clientY);

Lastly, modify the styling lines from using top and left to a single line with translate:

elmnt.style.transform = 'translate(' + (pos1) + "px, " + (pos2) + "px)";

Functional demonstration:

I updated the outdated var declaration to let since these variables are mutable.

//Enable dragging for the DIV element:
dragElement(document.getElementById("mydiv2"));
dragElement(document.getElementById("mydiv"));

function dragElement(elmnt) {
  let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // Obtain the initial mouse cursor position:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // Call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // Calculate the new cursor position:
    pos1 = pos1 - (pos3 - e.clientX);
    pos2 = pos2 - (pos4 - e.clientY);
    pos3 = e.clientX;
    pos4 = e.clientY;
    // Set the new position for the element:
    elmnt.style.transform = 'translate(' + (pos1) + "px, " + (pos2) + "px)";
  }

  function closeDragElement() {
    // Stop moving when the mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv,
#mydiv2 {
  width: 160px;
  background-color: #f1f1f1;
  text-align: center;
  cursor: move;
  border: 1px solid #d3d3d3;
}

#mydivheader,
#mydivheader2 {
  padding: 10px;
  background-color: #2196F3;
  color: #fff;
}
<div class="container">
  <div class="row">
    <div id="mydiv" class="col-sm-10">
      <div id="mydivheader">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
    <div id="mydiv2" class="col-sm-10">
      <div id="mydivheader2">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
  </div>
</div>

Descriptive variable names

The variable names used in the w3schools example pos1, pos2, pos3, pos4 might not provide clear context. It would be beneficial to rename them for better clarity: translate = { x: 0, y: 0 } and client = { x: 0, y: 0 }.

Functional demonstration:

//Enable dragging for the DIV element:
dragElement(document.getElementById("mydiv2"));
dragElement(document.getElementById("mydiv"));

function dragElement(elmnt) {
  let translate = { x: 0, y: 0 };
  let client = { x: 0, y: 0 };
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // Obtain the initial mouse cursor position:
    client.x = e.clientX;
    client.y = e.clientY;
    document.onmouseup = closeDragElement;
    // Call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // Calculate the new cursor position:
    translate.x = translate.x - (client.x - e.clientX);
    translate.y = translate.y - (client.y - e.clientY);
    client.x = e.clientX;
    client.y = e.clientY;
    // Set the new position for the element:
    elmnt.style.transform = 'translate(' + (translate.x) + "px, " + (translate.y) + "px)";
  }

  function closeDragElement() {
    // Stop moving when the mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv,
#mydiv2 {
  width: 160px;
  border: 1px solid #d3d3d3;
  text-align: center;
  cursor: move;
  background-color: #f1f1f1;
}

#mydivheader,
#mydivheader2 {
  padding: 10px;
  background-color: #2196F3;
  color: #fff;
}
<div class="container">
  <div class="row">
    <div id="mydiv" class="col-sm-10">
      <div id="mydivheader">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
    <div id="mydiv2" class="col-sm-10">
      <div id="mydivheader2">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
  </div>
</div>

Final version

For an integrated solution showcasing the functionality with dynamically added draggable components, refer to the following definitive implementation. I switched the outdated var declarations to const considering these variables remain constant.

Functional demonstration:

const d = ["module1", "module2", "module3", "module4", "module5"];

for(i in d) {
  append_module_name_to_drag_div(d[i]);
  dragElement(document.getElementById(d[i]));  
}
  
function append_module_name_to_drag_div(module_name) {
  const mydiv = document.createElement("div");
  mydiv.id = module_name;
  mydiv.className = 'module col-sm-10';

  const mydivmarker = document.createElement("div");
  mydivmarker.id = module_name+"header";
  mydivmarker.className = 'mydivheader';
  
  const marker_image = new Image();
  marker_image.src = "http://www.clker.com/cliparts/w/O/e/P/x/i/map-marker-hi.png";
  marker_image.height = 45;
  marker_image.width = 35;
  
  mydivmarker.append(marker_image);
  mydiv.innerHTML = module_name;
  mydiv.append(mydivmarker);
  document.getElementById("markers").appendChild(mydiv);
}

function dragElement(elmnt) {
  let translate = { x: 0, y: 0 };
  let client = { x: 0, y: 0 };
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // Obtain the initial mouse cursor position:
    client.x = e.clientX;
    client.y = e.clientY;
    document.onmouseup = closeDragElement;
    // Call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // Calculate the new cursor position:
    translate.x = translate.x - (client.x - e.clientX);
    translate.y = translate.y - (client.y - e.clientY);
    client.x = e.clientX;
    client.y = e.clientY;
    // Set the new position for the element:
    elmnt.style.transform = 'translate(' + (translate.x) + "px, " + (translate.y) + "px)";
  }

  function closeDragElement() {
    // Stop moving when the mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
.module {
  width: 80px;
  border: 1px solid #d3d3d3;
  text-align: center;
  cursor: move;
  background-color: #f1f1f1;
}

.mydivheader {
  padding: 10px;
  background-color: #2196F3;
  color: #fff;
}
<div class="container">
  <div id="markers" class="row">
  </div>
</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

Output in Typescript for the chosen option

A message can be sent based on the user's choice of either Now or Later. If the user selects Now, the message will be sent immediately. If Later is chosen, a date format option needs to be created for setting a future date. <label for=""& ...

The module cannot be located due to an error: Package path ./dist/style.css is not being exported from the package

I am facing an issue with importing CSS from a public library built with Vite. When I try to import the CSS using: import 'rd-component/dist/style.css'; I encounter an error during the project build process: ERROR in ./src/page/photo/gen/GenPhot ...

Optical Character Recognition (OCR) tool

Does anyone have recommendations for a JavaScript OCR API that can easily be accessed via JavaScript? I'm searching for an API similar to this: upload an image along with the desired result type (e.g. numbers, objects, text, mixed, etc) and receive t ...

I'm having trouble displaying the Facebook page plugin on my HTML website

I encountered a minor issue. I attempted to add the "Facebook page plugin" on a test website. I copied the code from the official Facebook plugin page: <!-- 1. Include the JavaScript SDK on your page once, ideally right after the opening <body> t ...

Trouble with Jsonp when using the getJSON function in jQuery

$(document).ready(function() { $.getJSON("http://quanta.net16.net/wordpressnew/test.php?jsoncallback=?", function(data) { alert('swag'); }); }); This is the JSON request I'm making and my data is properly contained within ?({object} ...

Tips for adding a "dynamic" backdrop in THREE.js

I stumbled upon this website: Notice how the gradient background changes with a 360 degree shading effect as you move the camera. Which aspect of THREE.js would be responsible for creating something like this? Could someone provide guidance on where to ...

Showcasing the selected menu item's value on an Angular button

I've encountered an issue where I have a list of menu items: <md-menu-item ng-value="menuItem.value" ng-repeat="menuItem in filtermenu.menuItems" ng-click="activeFilterCtrl.selectedfilter(menuItem)" translate> <md-button> {{ m ...

Obtain the content of a clicked item on the following page using NextJs

I am currently working on a nextjs app that displays a list of 10 movies on the homepage, each with a Button / Link that leads to a specific page for that movie where all its content is shown. Initially, I tried adding the movie id to the Link like this: ...

Passing User Authentication State from Express/Passport to a React Client: A Step-by-Step Guide

I want to pass a basic boolean value const isLoggedIn = true; (provided by Passport) from my Express backend to my React frontend. I'm looking for an alternative to embedding it in the HTML through a templating engine. This seems like a common req ...

ReactJS encounters errors even when the backend is returning a 200 OK status code

My ReactJS frontend receives an error when sending a GET request, even though the django backend terminal shows [10/Mar/2018 23:31:08] "GET /post/ HTTP/1.1" 200 930. I am using redux sagas in my project. In src/index.js: (index.js code snippet here) In ...

Passing variables through a promise chain and utilizing the finally method

My current endeavor involves constructing a log for an Express API, yet I am encountering difficulties in extracting the data for logging. I have successfully logged the initial req and res objects within the finally block, but I am uncertain about how to ...

Display information on the console from any location

I'm working on a Node.js project where I need to display text at specific locations, such as the top right corner. Is there a Node.js module available that can help me achieve this? Something like: var displayModule = require('display_module&ap ...

When adding text to a React Material UI Box, it can sometimes extend beyond the boundaries

I'm currently working on a straightforward React page, but I've encountered an issue right from the start: my text is overflowing the box and I can't figure out why. Could someone take a look at my styles? Here's a picture showing the ...

Searching for an element using Python's Selenium and JavaScript

on this page I am trying to click on the "Choose file" button but I keep getting the error message: javascript error: argument is not defined var1 = sys.argv[1] path = os.path.abspath(var1) driver.get("https://www.virustotal.com/gui/home/upload& ...

Attempting to transform a numerical value into CSS syntax

Currently, I am attempting to loop through several DIV elements, extract a numerical value from each DIV, and then based on that value matching a specific value in the JavaScript code, assign a particular CSS Class back to the original DIV. This is the sn ...

Incorporating information into an HTML webpage

Question: How can I create a dropdown menu in HTML with a list of languages (e.g. English, French) and display them based on the native language? I want to show the languages in their respective native language on the combo box (e.g. "french" displayed a ...

The CSS styles are not being rendered on the page

My simple static html/css project seems to be having trouble applying styles to the html elements in stackblitz. You can view the project here. I'm wondering if there might be an issue with linking the css file using the link tag, or perhaps it's ...

Function that observes with the pipe syntax

Is it possible to apply map, switchMap, or any other operator in conjunction with a function that outputs an observable? The objective here is to transform the result of the observable function and utilize that value during the subscription to another ob ...

Modify the size value dialog during runtime

I am attempting to create a property or method that can change the size of my container dialog built using Vuetify. Initially, I need it to look like this: <v-flex xs12 md10> I believe that by creating a variable property, I can dynamically change ...

Can I programmatically retrieve a comprehensive list of all global HTML attributes available?

Exploring the extensive list of global HTML attributes can be overwhelming, especially when considering how it continues to evolve with browser updates. Can JavaScript be leveraged to dynamically generate a complete list of these attributes without the ne ...