'JavaScript - How to make multiple divs draggable and movable across the website

I want to have multiple divs on my website to be draggable and movable across the website (or at least in the container which it's found).

I would like to clarify that I searched on the web and Stackoverflow multiple times, but all of them suggest answers by using jQuery or some other libraries. I would like a pure JavaScript way of moving multiple divs (hopefully with just one function for all) across the website.

The project that I'm making is to practice drag and drop with HTML and JS, I have 4 divs in a container 'x' (with images in them) and another container 'y' to drag and drop those divs to that container 'y', but if I drag them all there, the first div just sets to the top left corner and the rest under them (or beside each other if I set their display to inline-block). So, I want to make those divs movable on that container 'y' which I dragged into to move them around wherever I need. (They are also resizable).

The Drag and drop JavaScript and HTML code (Excluding CSS and some unnecessary HTML code) is this:

function allowDrop(ev) {
    ev.preventDefault();
}

function drag(ev) {
    ev.dataTransfer.setData("text", ev.target.id);
    ev.dataTransfer.dropEffect = "move";
}

function drop(ev) {
    ev.preventDefault();
    var data = ev.dataTransfer.getData("text");
    ev.target.appendChild(document.getElementById(data));
}
<div id="images-container" ondrop="drop(event)" ondragover="allowDrop(event)">

      <div class="resize" style="background: url('images/photo-1.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag1" draggable="true" ondragstart="drag(event)">
      </div>
      <div class="resize" style="background: url('images/photo-2.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag2" draggable="true" ondragstart="drag(event)">
      </div>
      <div class="resize" style="background: url('images/photo-3.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag3" draggable="true" ondragstart="drag(event)">
      </div>
      <div class="resize" style="background: url('images/photo-4.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag4" draggable="true" ondragstart="drag(event)">
      </div>

 </div>

 <div id="gallery" ondrop="drop(event)" ondragover="allowDrop(event)"></div>

I checked this article by W3schools, but it works with just one div element, as it's built with an ID, but what can I do with this code to use it for multiple divs and with classes I guess? If there is another way of doing it, I'll be glad to hear some suggestions about it, though in pure JavaScript.

Thanks.



Solution 1:[1]

I guess you probably sorted this by now. I will post this here for people like me who are searching for the answer. I thought that w3Schools example would only work for a single element too. It actually works on multiple elements just by calling the function on each one.

var draggableElements = document.getElementsByClassName("draggable");

for(var i = 0; i < draggableElements.length; i++){
    dragElement(draggableElements[i]);
}

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;
        pos3 = parseInt(e.clientX);
        pos4 = parseInt(e.clientY);
        document.onmouseup = closeDragElement;
        document.onmousemove = elementDrag;
        return false;
    }

    function elementDrag(e) {
        e = e || window.event;
        pos1 = pos3 - parseInt(e.clientX);
        pos2 = pos4 - parseInt(e.clientY);
        pos3 = parseInt(e.clientX);
        pos4 = parseInt(e.clientY);
        elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
        console.log(elmnt.offsetTop)
        elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
    }

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

Now at first this gave me odd results. My divs were only going in one direction when I moved the mouse, if you have margin on the divs it will mess up the calculations. So either don't use margin or edit the code to work for them.

Link to the original W3Schools tut: https://www.w3schools.com/howto/howto_js_draggable.asp

Solution 2:[2]

This works for me. No JQuery used.

I produces 4 divs all on top of each other but they can be dragged around. When you click on a div, its border lights up and you can drag it. The dragged div moves in front of the other divs. Because it looks fun, I've drawn curves between the divs using a canvas.

<!doctype html>
<html>
    <head>
        <style>
            div {
            position: absolute;
            top: 0;
            height: 60px;
            width: 100px;
            background: #15a5ed;
            text-align: center;
            line-height: 60px;
            border-radius: 15px 15px 15px 15px;
            }
        </style>
    </head>
    <body>
        <canvas id="canvas" class="canvas"></canvas>
        <div id="div1">Me</div>
        <div id="div2">You</div>
        <div id="div3">Us</div>
        <div id="div4">Them</div>
        <script>
            divs = document.getElementsByTagName("div");
            for (div of divs) div.onmousedown = onMouseDown;
            
            document.onmousemove = onMouseMove;
            document.onmouseup   = onMouseUp;
            
            canvas.width = window.innerWidth - 20;
            canvas.height = window.innerHeight - 20;
            
            var the_moving_div = ''; 
            var the_last_mouse_position = { x:0, y:0 };
            
            drawConnectors();
            
            function onMouseDown(e) {
                e.preventDefault();
                the_moving_div            = e.target.id;      // remember which div has been selected 
                the_last_mouse_position.x = e.clientX;        // remember where the mouse was when it was clicked
                the_last_mouse_position.y = e.clientY;
                e.target.style.border = "2px solid blue";     // highlight the border of the div
            
                var divs = document.getElementsByTagName("div");
                e.target.style.zIndex = divs.length;          // put this div  on top
                var i = 1; for (div of divs) if (div.id != the_moving_div) div.style.zIndex = i++;   // put all other divs behind the selected one
            }
            
            function onMouseMove(e) {
                e.preventDefault();
                if (the_moving_div == "") return;
                var d = document.getElementById(the_moving_div);
                d.style.left = d.offsetLeft + e.clientX - the_last_mouse_position.x + "px";     // move the div by however much the mouse moved
                d.style.top  = d.offsetTop  + e.clientY - the_last_mouse_position.y + "px";
                the_last_mouse_position.x = e.clientX;                                          // remember where the mouse is now
                the_last_mouse_position.y = e.clientY;
                drawConnectors();
            }
            
            function onMouseUp(e) {
                e.preventDefault();
                if (the_moving_div == "") return;
                document.getElementById(the_moving_div).style.border = "none";             // hide the border again
                the_moving_div = "";
            }
            
            function drawConnectors() {
                var canvas = document.getElementById('canvas');
                var ctx = canvas.getContext('2d');
                ctx.clearRect(0,0,canvas.width,canvas.height);
                ctx.beginPath();
                ctx.strokeStyle="#15a5ed";
                ctx.lineWidth=3;
                for (div1 of divs) for (div2 of divs) {
                    if (div1 == div2) continue;
                    ctx.moveTo(div1.offsetLeft + div1.clientWidth/2, div1.offsetTop + div1.clientHeight/2);
                    ctx.bezierCurveTo(div1.offsetLeft, div1.offsetTop, 
                                      div2.offsetLeft, div2.offsetTop, 
                                      div2.offsetLeft + div2.clientWidth/2, div2.offsetTop + div2.clientHeight/2);
                    ctx.stroke();                   
                }
            }
        </script>
    </body>
</html>

Solution 3:[3]

I have had a little play around with this. Using the w3 schools example. I have expanded on that to make it dynamic, (class selectable).

Here is the JavaScript to make it work.

document.addEventListener('DOMContentLoaded', function () {
    //Make the DIV element draggagle:

    members = document.querySelectorAll('.mover').forEach(item => {
        item.addEventListener('mousedown', ev => {
            console.log(ev);
            let tid;
            tid = ev.target.id;
            if (ev.target.id == "") {
                tid = ev.target.parentNode.id;
            }
            dragElement(document.getElementById(tid));
        })
    })

    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;
        }
    }
});

Noting the changes I have made:

  • DOM Lister wrapping the code.
  • mousedown listener assigned to the class ".mover"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Mister SirCode
Solution 3 CodingInTheUK