如何使用 JavaScript 从头开始​​实现拖放 div?

Posted

技术标签:

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

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

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

但是细节是什么?

【问题讨论】:

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

jQuery 方式:

查看jQueryUI 插件draggable 和droppable。

已经在 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】:
    使其绝对定位,具有高 z-index。 检查 div 的 onmousedown。 使用事件的 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>

【讨论】:

我建议从您的答案中删除 w3schools 链接,因为不应将他们的网站作为权威链接。 我强烈建议你不要使用这个 api:quirksmode.org/blog/archives/2009/09/the_html5_drag.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/***/company/img/logos/so/so-logo.png?v=9c558ec15d8a" draggable="true" ondragstart="drag(event)" id="drag1"  >
</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.preventDefaultdrop 事件中,必须调用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】:
    要将 div 置于其他元素之上,您必须为其指定高 z-index。此外,您可以设置 box-shadow 以向用户反馈该元素是可拖动的。 您总共需要监听三个事件:mousedownmouseup 鼠标移动。在 mousedown 上,您必须在 mousemove 上附加一个监听器,它会跟踪鼠标指针的移动并相应地移动 div ,并且在 mouseup 上,您必须删除 mousemove 上的侦听器。 用鼠标移动 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" > 
        </div> 
          
        <div id="imageNo2" class = "listitemClass"> 
            <img src="images/geeksimage2.png" > 
        </div> 
          
        <div id="imageNo3" class = "listitemClass"> 
            <img src="images/geeksimage3.png" > 
        </div> 
          
        <div id="imageNo4" class = "listitemClass"> 
            <img src="images/geeksimage4.png" > 
        </div> 
          
        <div id="imageNo5" class = "listitemClass"> 
            <img src="images/geeksimage5.png" > 
        </div> 
          
        <div id="imageNo6" class = "listitemClass"> 
            <img src="images/geeksimage6.png" > 
        </div> 
    </div> 
      
    <div id="outputDiv"> 
        <b>Output of ID's of images : </b> 
        <input id="outputvalues" type="text" value="" /> 
    </div> 
</body> 
  
</html>

【讨论】:

以上是关于如何使用 JavaScript 从头开始​​实现拖放 div?的主要内容,如果未能解决你的问题,请参考以下文章

从头开始实现朴素贝叶斯算法

如何从头开始调试反向传播实现?

如何在动态创建的 HTML 中使用拖放功能? (SortableJS)

如何使用 AWS CLI 从头开始​​创建堆栈?

JS从头开始

在JavaScript中从头开始创建空URL对象