JavaScript 如何通过点击和拖动来动态移动 Div

58

好的,看起来应该很简单。我需要根据鼠标在窗口中的位置移动一个已经存在的div。我已经搜索了所有内容,但结果都是过于复杂,都需要使用jquery。我需要严格使用javascript来完成我的目标。

方法:

var mousePosition;
var div;

(function createDiv(){

    div = document.createElement("div");
    div.style.position = "absolute";
    div.style.left = "0px";
    div.style.top = "0px";
    div.style.width = "100px";
    div.style.height = "100px";
    div.style.background = "red";
    div.style.color = "blue";

    div.addEventListener('mousedown', handleKeyPressed, true);

    document.body.appendChild(div);


})();

function handleKeyPressed(event) {

    event.preventDefault();

    mousePosition = {

        x : event.clientX,
        y : event.clientY

    };

    div.style.left = mousePosition.x;
    div.style.top = mousePosition.y;

    //alert("whoa!");

}

1
jQuery可以做到这一点,它非常适合各种事情。添加jQuery UI,您就可以轻松实现可拖动功能。 - adeneo
11
@adeneo:jQuery是一个JavaScript库,但我想学习的是纯JavaScript而非jQuery。虽然使用jQuery是常规做法,但目前不感兴趣学习它,只想研究纯JavaScript。 - StoneAgeCoder
9个回答

103

我认为你想要的更像是这样

var mousePosition;
var offset = [0,0];
var div;
var isDown = false;

div = document.createElement("div");
div.style.position = "absolute";
div.style.left = "0px";
div.style.top = "0px";
div.style.width = "100px";
div.style.height = "100px";
div.style.background = "red";
div.style.color = "blue";

document.body.appendChild(div);

div.addEventListener('mousedown', function(e) {
    isDown = true;
    offset = [
        div.offsetLeft - e.clientX,
        div.offsetTop - e.clientY
    ];
}, true);

document.addEventListener('mouseup', function() {
    isDown = false;
}, true);

document.addEventListener('mousemove', function(event) {
    event.preventDefault();
    if (isDown) {
        mousePosition = {

            x : event.clientX,
            y : event.clientY

        };
        div.style.left = (mousePosition.x + offset[0]) + 'px';
        div.style.top  = (mousePosition.y + offset[1]) + 'px';
    }
}, true);

FIDDLE


1
它有点卡顿。 - Ali Gajani
1
这是因为它总是在鼠标指针的左上角,你必须计算元素相对于你点击的位置的偏移量,但这几乎是一个问题本身。 - adeneo
1
@adeneo 谢谢。 :) 尽管您本可以直接称呼我为白痴,因为我忘记在 div.style.left = e.clientX; 的末尾添加 px。但这对我有所帮助,非常感谢。 - StoneAgeCoder
2
@StoneAgeCoder - 是的,在纯JS中需要单位。让我们看看能否修复那个抖动,等一下。 - adeneo
3
这绝对是我见过的最佳解决方案。 这个脚本很小而且易于理解,在不同的浏览器上比我见过的任何其他东西都要好。 我不得不评论和投票,只为了将来能够再次找到它。 - sanepete
显示剩余11条评论

22

支持触摸输入

所有其他答案(包括被接受的答案)均不支持触摸输入。 触摸输入具有不同于鼠标输入的事件。 请参阅MDN上的使用触摸事件

以下代码段即使在触摸输入下也能正常工作。我已经突出显示了需要添加的所有代码行以支持触摸设备。
基本思路是每个包含类列表中的draggable的元素都应该是可拖动的。当您有大量需要拖动的元素时,这个概念更容易理解。

请参见 Glitch 页面进行演示。

const d = document.getElementsByClassName("draggable");

for (let i = 0; i < d.length; i++) {
  d[i].style.position = "relative";
}

function filter(e) {
  let target = e.target;

  if (!target.classList.contains("draggable")) {
    return;
  }

  target.moving = true;

  //NOTICE THIS  Check if Mouse events exist on users' device
  if (e.clientX) {
    target.oldX = e.clientX; // If they exist then use Mouse input
    target.oldY = e.clientY;
  } else {
    target.oldX = e.touches[0].clientX; // Otherwise use touch input
    target.oldY = e.touches[0].clientY;
  }
  //NOTICE THIS  Since there can be multiple touches, you need to mention which touch to look for, we are using the first touch only in this case

  target.oldLeft = window.getComputedStyle(target).getPropertyValue('left').split('px')[0] * 1;
  target.oldTop = window.getComputedStyle(target).getPropertyValue('top').split('px')[0] * 1;

  document.onmousemove = dr;
  //NOTICE THIS 
  document.ontouchmove = dr;
  //NOTICE THIS 

  function dr(event) {
    event.preventDefault();

    if (!target.moving) {
      return;
    }
    //NOTICE THIS 
    if (event.clientX) {
      target.distX = event.clientX - target.oldX;
      target.distY = event.clientY - target.oldY;
    } else {
      target.distX = event.touches[0].clientX - target.oldX;
      target.distY = event.touches[0].clientY - target.oldY;
    }
    //NOTICE THIS 

    target.style.left = target.oldLeft + target.distX + "px";
    target.style.top = target.oldTop + target.distY + "px";
  }

  function endDrag() {
    target.moving = false;
  }
  target.onmouseup = endDrag;
  //NOTICE THIS 
  target.ontouchend = endDrag;
  //NOTICE THIS 
}
document.onmousedown = filter;
//NOTICE THIS 
document.ontouchstart = filter;
//NOTICE THIS 
.draggable {
  width: 100px;
  height: 100px;
  background: red;
}
<div class="draggable"></div>


9

检查这个是否比adeneo更流畅:FIDDLE

var m = document.getElementById('move');
m.addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);

function mouseUp() {
    window.removeEventListener('mousemove', move, true);
}

function mouseDown(e) {
    window.addEventListener('mousemove', move, true);
}

function move(e) {
    m.style.top = e.clientY + 'px';
    m.style.left = e.clientX + 'px';
};

1
我觉得这个比被接受的答案要差一些,没有那么流畅。 实际上有点卡顿。 我是不是做错了什么? - sanepete
1
是的,现在被接受的答案比我的更流畅。请使用被接受的答案。 - jhyap
你好,你能帮我解决这个问题吗?我无法使鼠标滚轮拖动功能起作用。https://stackoverflow.com/questions/70444738/disabled-move-a-dragdrop-element-when-scrolling-wheel - Lenin Zapata

7

我对 @adeneo 的非常好的答案进行了小修改。 如果将所有内容都封装在一个函数中,并将每个事件都附加到 div 上,那么它就可以作为库的一部分使用。

调用以下函数并传递一个 id。如果该 div 不存在,则会创建它。

function drag_div(div_id){
var div;

div = document.getElementById(div_id);

if(div == null){
   div = document.createElement("div");
   div.id = div_id;
   div.style.position = "absolute";
   div.style.left = "0px";
   div.style.top = "0px";
   div.style.width = "100px";
   div.style.height = "100px";
   div.style.background = "red";
   div.style.color = "blue";
   document.body.appendChild(div);
}

div.addEventListener('mousedown', function(e) {
    div.isDown = true;
    div.offset = [
        div.offsetLeft - e.clientX,
        div.offsetTop - e.clientY
    ];
}, true);

div.addEventListener('mouseup', function() {
    div.isDown = false;
}, true);

div.addEventListener('mousemove', function(event) {
    event.preventDefault();
    if (div.isDown) {
        div.mousePosition = {

            x : event.clientX,
            y : event.clientY

        };
        div.style.left = (div.mousePosition.x + div.offset[0]) + 'px';
        div.style.top  = (div.mousePosition.y + div.offset[1]) + 'px';
    }
}, true);
}

谢谢你提供的有关库的想法。但是当您快速移动鼠标时,divs会出现卡顿。应该将mousemove事件添加到文档中。 - J. Doe
你好,能帮我解决这个问题吗?我无法让鼠标滚轮拖动正常工作。https://stackoverflow.com/questions/70444738/disabled-move-a-dragdrop-element-when-scrolling-wheel - Lenin Zapata

2
这里有另一种包括触摸输入的方法。
dragElement(document.getElementById('mydiv'));

function dragElement(element) {
    var startX = 0, startY = 0, endX = 0, endY = 0;
    element.onmousedown = dragStart;
    element.ontouchstart = dragStart;

    function dragStart(e) {
        e = e || window.event;
        e.preventDefault();
        // mouse cursor position at start  
        if (e.clientX) {  // mousemove
            startX = e.clientX;
            startY = e.clientY;
        } else { // touchmove - assuming a single touchpoint
            startX = e.touches[0].clientX
            startY = e.touches[0].clientY
        }
        document.onmouseup = dragStop;
        document.ontouchend = dragStop;
        document.onmousemove = elementDrag;  // call whenever the cursor moves
        document.ontouchmove = elementDrag;
    }

    function elementDrag(e) {
        e = e || window.event;
        e.preventDefault();
        // calculate new cursor position
        if (e.clientX) {
            endX = startX - e.clientX;
            endY = startY - e.clientY;
            startX = e.clientX;
            startY = e.clientY;
        } else {
            endX = startX - e.touches[0].clientX;
            endY = startY - e.touches[0].clientY;
            startX = e.touches[0].clientX;
            startY = e.touches[0].clientY;
        }
        // set the new position
        element.style.left = (element.offsetLeft - endX) + "px";
        element.style.top = (element.offsetTop - endY) + "px";
    }

    function dragStop() {
        // stop moving on touch end / mouse btn is released 
        document.onmouseup = null;
        document.onmousemove = null;
        document.ontouchend = null;
        document.ontouchmove = null;
    }
}

2
您可以将其用作库。完美地工作。我在github上找到了它,但有时会卡住,因为共享者将"mouseup"放在元素上。我将其更改为文档,问题得到解决。这是修复后的版本。
'use strict';

/**
 * Makes an element draggable.
 *
 * @param {HTMLElement} element - The element.
 */
function draggable(element) {
    var isMouseDown = false;

    // initial mouse X and Y for `mousedown`
    var mouseX;
    var mouseY;

    // element X and Y before and after move
    var elementX = 0;
    var elementY = 0;

    // mouse button down over the element
    element.addEventListener('mousedown', onMouseDown);

    /**
     * Listens to `mousedown` event.
     *
     * @param {Object} event - The event.
     */
    function onMouseDown(event) {
        mouseX = event.clientX;
        mouseY = event.clientY;
        isMouseDown = true;
    }

    // mouse button released
    document.addEventListener('mouseup', onMouseUp);

    /**
     * Listens to `mouseup` event.
     *
     * @param {Object} event - The event.
     */
    function onMouseUp(event) {
        isMouseDown = false;
        elementX = parseInt(element.style.left) || 0;
        elementY = parseInt(element.style.top) || 0;
    }

    // need to attach to the entire document
    // in order to take full width and height
    // this ensures the element keeps up with the mouse
    document.addEventListener('mousemove', onMouseMove);

    /**
     * Listens to `mousemove` event.
     *
     * @param {Object} event - The event.
     */
    function onMouseMove(event) {
        if (!isMouseDown) return;
        var deltaX = event.clientX - mouseX;
        var deltaY = event.clientY - mouseY;
        element.style.left = elementX + deltaX + 'px';
        element.style.top = elementY + deltaY + 'px';
    }
}

1

jQuery更容易部署。我很惊讶你说你不想学习它。

您可以将jQuery文件保存在本地计算机中,因此无需互联网即可使用jQuery功能。

在我的情况下,我已将其保存在工具文件夹中。所以我不需要上网。

对于所有的JS代码行,您只需要一行小型代码即可。

 <script src="/common/tools/jquery-1.10.2.js"></script>
 <script src="/common/tools/jquery-ui.js"></script>

 <script>
   $(function() {
   $( "#mydiv_to_make_draggable" ).draggable();
   });
</script>

14
JQuery很容易使用,但任何人都可以使用它。我也是一个喜欢从头开始编写所有代码的程序员。如果您在代码中包含JQuery库,并且仅使用其中一两个功能,那么将浪费很多空间。 - Vince
3
现在有太多人直接使用jQuery,他们不了解或者不认识为什么jQuery一开始存在,也没有对当前的环境进行评估,看是否需要如此庞大的代码库。往往,使用纯ES6并进行ES5转译将会得到未来可靠的代码、更少的冗余和更好的编程理解,而不仅仅是让它“工作”。 - Markus

1

虽然来晚了,但我发现这是一个更简单和适合触摸的实现方式:

  • 使用 "pointerdown/move/up" 事件。此外,无需不断监听 mousemove 事件并使用布尔值如 isMove 等。相反,在 pointerdown 上附加 move 和 up 事件,并在 pointerup 上使用 Element.removeEventListener 从 window 中分离侦听器。
  • 使用 CSS touch-action: none; 使指针事件在触摸设备上工作
  • 不要存储拖动开始坐标,而是在元素的当前 Element.offsetLeft/Top 上使用 Event.movementX,Y

const drag = (evt) => {
  
  const el = evt.currentTarget;
  el.style.touchAction = "none";
  
  const move = (evt) => {
    el.style.left = `${el.offsetLeft + evt.movementX}px`;
    el.style.top = `${el.offsetTop + evt.movementY}px`;
  };
  
  const up = () => {
    removeEventListener("pointermove", move);
    removeEventListener("pointerup", up);
  };
  
  addEventListener("pointermove", move);
  addEventListener("pointerup", up);
};


// Use like:
document.querySelector("#box").addEventListener("pointerdown", drag);
#box { position: absolute; width: 50px; height: 50px; background: red; }
<div id="box"></div>


1

添加触摸的已接受答案

adeneo 的已接受答案非常优雅且有效。但是它仅适用于鼠标点击,因此这里提供了一个修改版,包括触摸输入:

var position;
var offset = [0,0];
var isDown = false;

function makeDraggable(el){

    ['mousedown', 'touchstart'].forEach( evt => 
        el.addEventListener(evt, pickup, true)
    );
    
    ['mousemove', 'touchmove'].forEach( evt => 
        el.addEventListener(evt, move, true)
    );

    ['mouseup', 'touchend'].forEach( evt => 
        el.addEventListener(evt, drop, true)
    );      
        
    function pickup(e) {
        isDown = true;
        if (e.clientX) {
            offset = [el.offsetLeft - e.clientX, el.offsetTop - e.clientY];
        }
        else if (e.touches) {  
            // for touch devices, use 1st touch only
            offset = [el.offsetLeft - e.touches[0].pageX, el.offsetTop - e.touches[0].pageY];
        }       
    }
    function move(e) {
        if (isDown) {
            if (e.clientX) {
                position = {x : e.clientX, y : e.clientY};
            }
            else if (e.touches) {
                position = {x : e.touches[0].pageX, y : e.touches[0].pageY};            
            }           
            el.style.left = (position.x + offset[0]) + 'px';
            el.style.top  = (position.y + offset[1]) + 'px';
        }
    }
    function drop(e) {
        // seems not to be needed for Android Chrome
        // and modern browsers on Mac & PC
        // but is required for iPad & iPhone
        isDown = false;     
        el.style.left = (position.x + offset[0]) + 'px';
        el.style.top  = (position.y + offset[1]) + 'px';
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接