【问题标题】:How to implement a drag-and-drop div from scratch with JavaScript?如何使用 JavaScript 从头开始​​实现拖放 div?
【发布时间】:2010-11-05 14:45:36
【问题描述】:

它应该是 CSS 和 JavaScript 的组合。要做的步骤应该是:

  1. 将其置于所有其他元素之上(指定哪个属性?)
  2. 点击事件时捕捉事件(监听哪个事件?)
  3. 在鼠标移动时移动 div。

但是细节是什么?

【问题讨论】:

  • 我意识到这是一个老问题,但我注意到您尚未接受答案,我正在尝试做类似的事情,所以我想知道哪个答案最适合您?

标签: javascript html css drag-and-drop


【解决方案1】:

jQuery 方式:

查看jQueryUI 插件draggabledroppable

已经在 jQuery 框架上投入了数百个小时,以使此类复杂的任务变得微不足道。利用 jQuery 团队的努力,让我们所有人更轻松地编写丰富的跨浏览器应用程序;)

查克·诺里斯的方式:

如果您坚持使用原始 javascript 进行尝试。你会想做一些事情。一,以编程方式将所有可拖动项目设置为相对/绝对定位。如果单击特定项目,请使其在 CSS 中的顶部/左侧值反映鼠标 x、y 轴所做的更改,直到释放单击为止。此外,您需要更新每个可拖动对象的 z-index,当它被单击以将其显示在视图中时。

教程:How to Drag and Drop with Javascript

【讨论】:

  • +1 表示“Chuck Norris' Way”,从现在开始我将在任何地方使用它。 :-)
  • 感谢您的更新。我想使用原始 javascript 的原因是我这样做是为了学习。我想我已经列出了实现它的工作,但不要不知道更详细的步骤:(
  • 最佳链接是luke.breuer.com/tutorial/javascript-drag-and-drop-tutorial.aspx。此答案中的链接和 david flanagan 的链接都无法处理 IE 中的 ondragstart 事件,这将导致 IE 出现问题。
【解决方案2】:
  1. 使其绝对定位,具有高 z-index。
  2. 检查 div 的 onmousedown。
  3. 使用事件的 mouseX 和 mouseY 属性来移动 div。

这是来自Javascript, the Definitive Guide 的示例(已更新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';
 }

【讨论】:

  • 你能提供一个原型演示吗?
  • @Shore,我以为你想远离框架? (假设您指的是 Prototype,即框架)。
  • 对,我想远离框架。事情一开始很容易,但以后也容易出乎意料..
  • +1 我不知道这是不是他要找的代码,我正在寻找这段很棒的代码,谢谢分享。我不明白你为什么要使用 setCapture,但它是他伟大的代码。
【解决方案3】:

HTML5 拖放

如果您在 2017 年或之后阅读本文,您可能想看看 HTML5 Drag and Drop API:

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

例子:

<!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>

【讨论】:

【解决方案4】:

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>

【讨论】:

    【解决方案5】:

    标准的拖放 API 被 suck big hairy donkey balls 广泛认可。所以我不建议从头开始做。但既然这是你的问题,那么制作可拖动的东西有一组要求,以及正确设置拖放区的一组要求:

    拖动:

    • dom 节点必须将“可拖动”属性设置为 true

    注意:e.dataTransfer.setDragImage可用于设置备用拖动图片(默认为被拖动dom节点的透明图片。

    注意2:e.dataTransfer.setData可以在dragstart事件中使用,设置一些可以从drop事件中取回的数据。

    掉落:

    • dragover 事件中,必须调用e.preventDefault
    • drop 事件中,必须调用e.preventDefault

    例子:

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

    但是,使用这个 API 有很多陷阱,包括:

    • 要区分拖放区上的 dragmove 事件和与可拖动项目相关的 dragmove 事件需要做很多工作
    • dragmove 即使你的鼠标没有移动也会触发
    • dragleavedragenter 即使您的鼠标没有移入或移出侦听 dom 节点也会触发(由于某些愚蠢的原因,它会在越过父子边界时触发)
    • 还有更多..

    更好的方法

    我编写了一个拖放库,它使使用标准拖放 API 变得更加容易,而没有所有这些陷阱。在这里查看:

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

    【讨论】:

    • @RedWei 可能有人被我的语言冒犯了。中学毕业 20 年后,我仍然被孩子们包围着。 :p
    • @BT 有人不得不拯救这场投票灾难#upvoted ;)
    【解决方案6】:

    是的,如果你想要一个拥有比你需要的更多功能的臃肿库,你可以使用 jQuery!或者,如果您想成为更多精英,请使用 Waltern Zorn's drag and drop library,它的大小只有十分之一。

    【讨论】:

    • 很高兴知道 walterzorn.com 网站在哪里结束,自 2010 年 7 月以来它已经关闭了一段时间 [ref. forums.netobjects.com/… 还是挂了!!!我在链接中说他死了,是真的吗?
    • @Marco 我相信。可惜他的工作没有继续下去。
    • 谢谢,我还找到了一个比你的版本更新的版本并粘贴在这里:jsfiddle.net/ADpX6/2
    • 我不相信它会小十倍。也许两次?
    • Waltern Zorn 链接仍然断开
    【解决方案7】:
    1. 要将 div 置于其他元素之上,您必须为其指定高 z-index。此外,您可以设置 box-shadow 以向用户反馈该元素是可拖动的。
    2. 您总共需要监听三个事件:mousedownmouseup 鼠标移动。在 mousedown 上,您必须在 mousemove 上附加一个监听器,它会跟踪鼠标指针的移动并相应地移动 div ,并且在 mouseup 上,您必须删除 mousemove 上的侦听器。
    3. 用鼠标移动 div 有点棘手。如果您翻译 div 到指针的位置,指针将始终指向 div 的左上角,即使您单击右下角也是如此。为此,您必须在 mousedown 事件处理程序中计算 div(左上角)和鼠标指针之间的坐标差。然后,您必须在 mousemove 事件处理程序中将 div 转换到该位置之前从鼠标位置减去该差异。

    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>
    

    【讨论】:

      【解决方案8】:

      您可以使用以下代码来做到这一点

      $(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>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-09
        • 1970-01-01
        • 2011-09-18
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多