omg
omg

Reputation: 140112

How to implement a drag-and-drop div from scratch with JavaScript?

It should be a combination of CSS and JavaScript. The steps to do should be:

  1. Make it on top of all other elements (which property to specify?)
  2. Catch the event when it is clicked (which event to listen to?)
  3. Move the div as mouse moves.

But what are the details?

Upvotes: 27

Views: 79966

Answers (9)

Adham Zahran
Adham Zahran

Reputation: 2180

w3schools got a nice example: https://www.w3schools.com/howto/howto_js_draggable.asp

HTML:

<!-- Draggable DIV -->
<div id="mydiv">
  <!-- Include a header DIV with the same name as the draggable DIV, followed by "header" -->
  <div id="mydivheader">Click here to move</div>
  <p>Move</p>
  <p>this</p>
  <p>DIV</p>
</div>

CSS:

#mydiv {
  position: absolute;
  z-index: 9;
  background-color: #f1f1f1;
  border: 1px solid #d3d3d3;
  text-align: center;
}

#mydivheader {
  padding: 10px;
  cursor: move;
  z-index: 10;
  background-color: #2196F3;
  color: #fff;
}

JS:

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

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

Upvotes: 0

Deepak
Deepak

Reputation: 313

You can do this by using following code

$(function() { 
        $("#imageListId").sortable({ 
            update: function(event, ui) { 
                    getIdsOfImages(); 
                } //end update          
        }); 
    }); 
  
    function getIdsOfImages() { 
        var values = []; 
        $('.listitemClass').each(function(index) { 
            values.push($(this).attr("id") 
                        .replace("imageNo", "")); 
        }); 
        $('#outputvalues').val(values); 
    }
/* text align for the body */
    body { 
        text-align: center; 
    } 
      
    /* image dimension */
    img { 
        height: 200px; 
        width: 350px; 
    } 
      
    /* imagelistId styling */
    #imageListId { 
        margin: 0; 
        padding: 0; 
        list-style-type: none; 
    } 
       
    #imageListId div { 
        margin: 0 4px 4px 4px; 
        padding: 0.4em; 
        display: inline-block; 
    } 
      
    /* Output order styling */
    #outputvalues { 
        margin: 0 2px 2px 2px; 
        padding: 0.4em; 
        padding-left: 1.5em; 
        width: 250px; 
        border: 2px solid dark-green; 
        background: gray; 
    } 
       
    .listitemClass { 
        border: 1px solid #006400; 
        width: 350px; 
    } 
       
    .height { 
        height: 10px; 
    }
<link href="https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>

<!DOCTYPE html> 
<html> 
<head> 
    <title> 
        Drag Drop feature
    </title> 
</head> 
       
<body> 
    <h1 style="color:green">GeeksforGeeks</h1>  
      
    <b>Drag and drop using jQuery UI Sortable</b> 
      
    <div class="height"></div><br> 
      
    <div id = "imageListId"> 
        <div id="imageNo1" class = "listitemClass"> 
            <img src="images/geeksimage1.png" alt=""> 
        </div> 
          
        <div id="imageNo2" class = "listitemClass"> 
            <img src="images/geeksimage2.png" alt=""> 
        </div> 
          
        <div id="imageNo3" class = "listitemClass"> 
            <img src="images/geeksimage3.png" alt=""> 
        </div> 
          
        <div id="imageNo4" class = "listitemClass"> 
            <img src="images/geeksimage4.png" alt=""> 
        </div> 
          
        <div id="imageNo5" class = "listitemClass"> 
            <img src="images/geeksimage5.png" alt=""> 
        </div> 
          
        <div id="imageNo6" class = "listitemClass"> 
            <img src="images/geeksimage6.png" alt=""> 
        </div> 
    </div> 
      
    <div id="outputDiv"> 
        <b>Output of ID's of images : </b> 
        <input id="outputvalues" type="text" value="" /> 
    </div> 
</body> 
  
</html>

Upvotes: -1

Shahrukh Haider
Shahrukh Haider

Reputation: 474

  1. To bring the div on top of other elements you have to assign it a high z-index. Additionally, you can set box-shadow to give a feedback to the user that the element is draggable.
  2. You have to listen for a total of three events: mousedown, mouseup, and mousemove. On mousedown you have to attach a listener on mousemove, which tracks the mouse pointer movements and moves the div accordingly, and on mouseup you have to remove the listener on mousemove.
  3. Moving the div with the mouse is a bit tricky. If you translate the div to the pointer's position, the pointer will always point to the top left corner of the div, even when you click at the bottom right corner. For this, you have to calculate the coordinate difference between the div (top left corner) and the mouse pointer, in the mousedown event handler. Then, you have to subtract that difference from the mouse position before translating the div to that position, in the mousemove event handler.

See the demo for a better idea.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      body,
      html {
        width: 100%;
        height: 100%;
        padding: 0px;
        margin: 0px;
      }
      #box {
        width: 100px;
        height: 100px;
        margin: auto;
        background-color: lightblue;
      }
      #box:active {
        border: 1px solid black;
        box-shadow: 2px 2px 5px 5px #bbb6b6;
      }
    </style>
  </head>
  <body>
    <div id="box"></div>
  </body>
  <script>
    var box = document.getElementById("box");
    var diff = {};
    var getBoxPos = function() {
      return {
        x: box.getBoundingClientRect().x,
        y: box.getBoundingClientRect().y
      };
    };
    var calcDiff = function(x, y) {
      var boxPos = getBoxPos();
      diff = {
        x: x - boxPos.x,
        y: y - boxPos.y
      };
    };
    var handleMouseMove = function(event) {
      var x = event.x;
      var y = event.y;
      x -= diff.x;
      y -= diff.y;

      console.log("X " + x + " Y " + y);
      box.style.position = "absolute";
      box.style.transform = "translate(" + x + "px ," + y + "px)";
    };

    box.addEventListener("mousedown", function(e) {
      calcDiff(e.x, e.y);
      box.addEventListener("mousemove", handleMouseMove, true);
    });

    box.addEventListener("mouseup", function(e) {
      console.log("onmouseup");
      box.removeEventListener("mousemove", handleMouseMove, true);
    });
  </script>
</html>

Upvotes: 1

B T
B T

Reputation: 61023

The standard Drag and Drop API is widely recognized to suck big hairy donkey balls. So I wouldn't recommend doing it from scratch. But since that's your question, there are one set of requirements for making something draggable, and one set of requirements for properly setting up a drop zone:

Dragging:

  • The dom node must have the "draggable" property set to true

Note: e.dataTransfer.setDragImage can be used to set an alternate drag image (the default is a transparent image of the dom node being dragged.

Note2: e.dataTransfer.setData can be used inside the dragstart event to set some data that can be gotten back from the drop event.

Dropping:

  • In the dragover event, e.preventDefault must be called
  • In the drop event, e.preventDefault must be called

Example:

<body>
    <div id="dragme" draggable="true">Drag Me</div>
    <div id="dropzone">Drop Here</div>
</body>

<script>
    var dragme = document.getElementById('dragme')
    var dropzone = document.getElementById('dropzone')

    dragme.addEventListener('dragstart',function(e){
        dropzone.innerHTML = "drop here"
    })

    dropzone.addEventListener('dragover',function(e){
        e.preventDefault()
    })
    dropzone.addEventListener('drop',function(e){
        e.preventDefault()
        dropzone.innerHTML = "dropped"
    })
</script>

However, there are a whole lot of gotchas in using this API, including that:

  • it takes a lot of work to distinguish between a dragmove event over a dropzone and a dragmove event related to a draggable item
  • dragmove fires even if your mouse isn't moving
  • dragleave and dragenter fire even if your mouse isn't moving in or out of the listening dom node (it fires whenever it crosses a child-parent bounary for some stupid reason)
  • And more..

A better way

I wrote a drag and drop library that makes it a ton easier to use the standard drag and drop API without all those gotchas. Check it out here:

https://github.com/fresheneesz/drip-drop

Upvotes: 6

Waruna Manjula
Waruna Manjula

Reputation: 3497

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

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

function drop(ev) {
    ev.preventDefault();
    var data = ev.dataTransfer.getData("text");
    ev.target.appendChild(document.getElementById(data));
}
.mydiv {
    float: left;
    width: 100px;
    height: 35px;
    margin: 10px;
    padding: 10px;
    border: 1px solid black;
}
<!DOCTYPE HTML>
<html>
<head>


</head>
<body>

<h2>Drag and Drop</h2>


<div id="div1" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)">
  <img src="https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a" draggable="true" ondragstart="drag(event)" id="drag1" width="88" height="31">
</div>

<div id="div2" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<div id="div3" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<div id="div4" class="mydiv" ondrop="drop(event)" ondragover="allowDrop(event)"></div>

</body>
</html>

Upvotes: 4

maechler
maechler

Reputation: 1367

HTML5 Drag and Drop

If you are reading this in the year 2017 or later, you might want to have a look at the HTML5 Drag and Drop API:

https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API

Example:

<!DOCTYPE HTML>
<html>
<head>
    <script>
    function allowDrop(ev) {
        ev.preventDefault();
    }

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

    function drop(ev) {
        ev.preventDefault();
        var data = ev.dataTransfer.getData("text");
        ev.target.appendChild(document.getElementById(data));
    }
    </script>
    <style>
        .draggable {
            border: 1px solid black;
            width: 30px;
            height: 20px;
            float: left;
            margin-right: 5px;
        }
        #target {
            border: 1px solid black;
            width: 150px;
            height: 100px;
            padding: 5px;
        }
    </style>
</head>
<body>

    <h1>Drag and Drop</h1>

    <h2>Target</h2>
    <div id="target" ondrop="drop(event)" ondragover="allowDrop(event)"></div>

    <h2>Draggable Elements</h2>
    <div id="draggable1" class="draggable" draggable="true" ondragstart="drag(event)"></div>
    <div id="draggable2" class="draggable" draggable="true" ondragstart="drag(event)"></div>
    <div id="draggable3" class="draggable" draggable="true" ondragstart="drag(event)"></div>

</body>
</html>

Upvotes: 6

Zack Marrapese
Zack Marrapese

Reputation: 12091

  1. make it absolute positioned, with a high z-index.
  2. check for onmousedown of the div.
  3. use the event's mouseX and mouseY attributes to move the div.

Here's an example from Javascript, the Definitive Guide (updated here):

/**
 *  Drag.js:    drag absolutely positioned HTML elements.
 *
 *  This module defines a single drag() function that is designed to be called
 *  from an onmousedown event handler. Subsequent mousemove event will
 *  move the specified element. A mouseup event will terminate the drag.
 *  If the element is dragged off the screen, the window does not scroll.
 *  This implementation works with both the DOM Level 2 event model and the
 *  IE event model.
 *
 *  Arguments:
 *
 *      elementToDrag: the element that received the mousedown event or
 *          some containing element. It must be absolutely positioned. Its
 *          style.left and style.top values will be changed based on the user's
 *          drag.
 *
 *      event: ethe Event object for the mousedown event.
 *
 *  Example of how this can be used:
 *      <script src="Drag.js"></script> <!-- Include the Drag.js script -->
 *      <!-- Define the element to be dragged -->
 *      <div style="postion:absolute; left:100px; top:100px; width:250px;
 *                  background-color: white; border: solid black;">
 *      <!-- Define the "handler" to drag it with. Note the onmousedown attribute. -->
 *      <div style="background-color: gray; border-bottom: dotted black;
 *                  padding: 3px; font-family: sans-serif; font-weight: bold;"
 *          onmousedown="drag(this.parentNode, event);">
 *      Drag Me <!-- The content of the "titlebar" -->
 *      </div>
 *      <!-- Content of the draggable element -->
 *      <p>This is a test. Testing, testing, testing.<p>This is a test.<p>Test.
 *      </div>
 *
 *  Author: David Flanagan; Javascript: The Definitive Guide (O'Reilly)
 *  Page: 422
 **/
 function drag(elementToDrag, event)
 {
     // The mouse position (in window coordinates)
     // at which the drag begins
     var startX = event.clientX, startY = event.clientY;

     // The original position (in document coordinates) of the
     // element that is going to be dragged. Since elementToDrag is
     // absolutely positioned, we assume that its offsetParent is the
     //document bodt.
     var origX = elementToDrag.offsetLeft , origY = elementToDrag.offsetTop;

     // Even though the coordinates are computed in different
     // coordinate systems, we can still compute the difference between them
     // and use it in the moveHandler() function. This works because
     // the scrollbar positoin never changes during the drag.
     var deltaX = startX - origX, deltaY = startY - origY;

     // Register the event handlers that will respond to the mousemove events
     // and the mouseup event that follow this mousedown event.
     if (document.addEventListener) //DOM Level 2 event model
     {
         // Register capturing event handlers
         document.addEventListener("mousemove", moveHandler, true);
         document.addEventListener("mouseup", upHandler, true);
     }
     else if (document.attachEvent) //IE 5+ Event Model
     {
         //In the IE event model, we capture events by calling
         //setCapture() on the element to capture them.
         elementToDrag.setCapture();
         elementToDrag.attachEvent("onmousemove", moveHandler);
         elementToDrag.attachEvent("onmouseup", upHandler);
         // Treat loss of mouse capture as a mouseup event.
         elementToDrag.attachEvent("onclosecapture", upHandler);
     }
     else //IE 4 Event Model
     {
         // In IE 4, we can't use attachEvent() or setCapture(), so we set
         // event handlers directly on the document object and hope that the
         // mouse event we need will bubble up.
         var oldmovehandler = document.onmousemove; //used by upHandler()
         var olduphandler = document.onmouseup;
         document.onmousemove = moveHandler;
         document.onmouseup = upHandler;
     }

     // We've handled this event. Don't let anybody else see it.
     if (event.stopPropagation) event.stopPropagation();    //  DOM Level 2
     else event.cancelBubble = true;                        //  IE

     // Now prevent any default action.
     if (event.preventDefault) event.preventDefault();      //  DOM Level 2
     else event.returnValue = false;                        //  IE

     /**
      * This is the handler that captures mousemove events when an element
      * is being dragged. It is responsible for moving the element.
      **/
      function moveHandler(e)
      {
          if (!e) e = window.event; //  IE Event Model

          // Move the element to the current mouse position, adjusted as
          // necessary by the offset of the initial mouse-click.
          elementToDrag.style.left = (e.clientX - deltaX) + "px";
          elementToDrag.style.top = (e.clientY - deltaY) + "px";

          // And don't let anyone else see this event.
          if (e.stopPropagation) e.stopPropagation();       // DOM Level 2
          else e.cancelBubble = true;                       // IE
      }

      /**
       * This is the handler that captures the final mouseup event that
       * occurs at the end of a drag.
       **/
       function upHandler(e)
       {
           if (!e) e = window.event;    //IE Event Model

           // Unregister the capturing event handlers.
           if (document.removeEventListener) // DOM event model
            {
                document.removeEventListener("mouseup", upHandler, true);
                document.removeEventListener("mousemove", moveHandler, true);
            }
            else if (document.detachEvent)  //  IE 5+ Event Model
            {
                elementToDrag.detachEvent("onlosecapture", upHandler);
                elementToDrag.detachEvent("onmouseup", upHandler);
                elementToDrag.detachEvent("onmousemove", moveHandler);
                elementToDrag.releaseCapture();
            }
            else    //IE 4 Event Model
            {
                //Restore the original handlers, if any
                document.onmouseup = olduphandler;
                document.onmousemove = oldmovehandler;
            }

            //  And don't let the event propagate any further.
            if (e.stopPropagation) e.stopPropagation(); //DOM Level 2
            else e.cancelBubble = true;                 //IE
       }
 }

 function closeMe(elementToClose)
 {
     elementToClose.innerHTML = '';
     elementToClose.style.display = 'none';
 }

 function minimizeMe(elementToMin, maxElement)
 {
     elementToMin.style.display = 'none';
 }

Upvotes: 16

Josh Stodola
Josh Stodola

Reputation: 82523

Yeah, you can use jQuery if you want a bloated library with far more functions than you need! Or if you want to be more of an elitist, use Waltern Zorn's drag and drop library, which is one tenth of the size.

Upvotes: 2

Sampson
Sampson

Reputation: 268492

The jQuery Way:

Check out the jQueryUI addons draggable and droppable.

Literally hundreds of hours have been invested into the jQuery framework to make complicated tasks like this almost trivial. Take advantage of the jQuery team's efforts to make programming rich cross-browser applications easier on us all ;)

Chuck Norris' Way:

If you insist on trying this with raw javascript. You'll want to do a few things. One, programmatically set all draggable items to a relative/absolute positioning. If you click a particular item, cause it's top/left values in CSS to reflect the changes made by the x,y axis of the mouse until the click is released. Additionally, you'll want to update the z-index of each draggable when it's clicked to bring it into view.

Tutorial: How to Drag and Drop with Javascript

Upvotes: 39

Related Questions