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

Body section CSS selector

Can a CSS selector be included within the body section of an HTML document? Here's an example of my code (although it is not functioning as expected): <html> <head> </head> <body> <div style= "a[target=_blank] {backgroun ...

Issue encountered while configuring server using express.js

Here is the server.js file I am working on, but I encounter a specific error when trying to set up the server with Express.js var express = require('express'); var app = express(); var PORT = process.env.PORT || 3000; app.all('/*', ...

Is there a way to eliminate the transform style from a draggable element?

I am looking to enhance the CDK drag and drop example by adding a preview of the dragged element with specific left and top positions, without utilizing the transform style. HTML <div class="example-boundary"> <div class="example- ...

What is the process for sending text messages in a local dialect using node.js?

Having an issue with sending bulk SMS using the textlocal.in API. Whenever I try to type a message in a regional language, it displays an error: {"errors":[{"code":204,"message":"Invalid message content"}],"status":"failure"} Is there a workaround for se ...

AngularJS causing issues with Materializecss dropdown menu operation

I'm currently working on a web application using Materializecss and AngularJS for the front-end. However, I've encountered an issue with the dropdown menu component of Materialize not functioning as expected. Here's an example of the dropdo ...

Storing files offline in Firefox 3.5 with file:// protocol

While experimenting with the code for offline storage in Firefox 3.5, I referred to a tutorial on . When the page loads, I am prompted with a dialog asking to store data, but after clicking Allow, the dialog does not disappear. The application functions co ...

Incorporating a YouTube channel into mobile websites

While it's relatively easy to embed single YouTube videos in mobile pages with the help of Google, I'm still struggling with embedding a whole channel. The issue seems to revolve around the screen width, and my attempts at using JavaScript have n ...

Invalid web address entered

While attempting to incorporate a functionality that opens a new window to a specific link when clicking an icon, I encountered an issue where the click action redirects to the wrong URL. Instead of directing to the URL specified in its href attribute, it ...

Why am I seeing numbers in the output when I log the data from the res.write(data) function in Node.js?

While working with Node.js on my Raspberry Pi, I encountered an issue where reading a local file 'test.html' resulted in hex output instead of the expected HTML format. Can someone explain why this might be happening? Additionally, I am aware tha ...

ConfirmUsername is immutable | TypeScript paired with Jest and Enzyme

Currently, I am experimenting with Jest and Enzyme on my React-TS project to test a small utility function. While working on a JS file within the project, I encountered the following error: "validateUsername" is read-only. Here is the code for the utilit ...

Troubleshooting issues with browser scrollbars in a Silverlight application (HTML)

I am currently working on a simple silverlight application and I need to incorporate web browser scroll bars into it (scroll bars should not be inside my silverlight app). This is the HTML code I have: <style type="text/css"> html, body { heigh ...

The functionality of bootstrap.styl malfunctions during the parsing process in stylus

I am attempting to incorporate stylus, a css preprocessor, with twitter bootstrap version 2.04. Upon downloading boostrap, I execute the command "stylus --css < bootstrap.css > bootstrap.styl" to generate a bootstrap.styl file. However, upon trying to p ...

Does the media query max-width refer to the size of the viewport or the size of the window?

Can someone clarify whether the max-width in a media query is based on the viewport size or window size? For instance, consider this media query: @media screen and (max-width: 360px){} Would this media query be activated when the viewport reaches 360px ...

show tab focus outline only

In search of a straightforward and effective method for focusable elements to display an outline only when the tab key is pressed, without showing it when using a mouse in React applications. (looking for something similar to :focus-visible that function ...

The use of DIV tags allows the element to be displayed in an inline

In my project, I decided to add three div tags. The first two are positioned next to each other with the third div tag placed below them. However, when I tried to insert a slideshow into the first div tag, all of the div tags ended up displaying as "block" ...

A step-by-step guide to moving Vue .prototype to its own file

I have a couple of functions that I need to add to Vue's .prototype. However, I don't want to clutter up the main.js file. I attempted to move the prototypes like this: //main.js import "./vue-extensions/prototypes"; ...

Personalize the tooltip feature in highchart

I am looking to enhance the tooltip feature in HighChart. Currently, on hover of my bar chart, only one value is displayed in the tooltip. However, I would like to display three values instead. Here is a snippet of my code: $(function () { $( ...

Guide on implementing text/ng-template script section in jsfiddle

My current challenge involves creating a jsfiddle that utilizes AngularJS to display two calendar controls. While my code functions properly when run locally, I've encountered an issue with including the template code via a script tag on jsfiddle: ht ...

Tips for customizing the jQuery countdown styling

Currently, I have incorporated the jQuery countdown into my project which consists of solely the essential Javascript required for the countdown to function. However, I am interested in achieving a similar visually appealing "animated" style similar to t ...

What is the best approach to storing and retrieving special characters ('+<>$") from a textfield into a database using PHP?

I have a form where users can enter a personal message with a subject. The data entered in the textarea is passed to a Javascript/jQuery function, which then sends it to a PHP file for storage in a database. However, I am encountering issues when special c ...