Javascript实现元素的拖拽效果

参考:https://juejin.cn/post/6844904158273765384

一、概述

在日常开发中我们都会遇见一些拖拽DOM元素的需求,本文总结了如何实现DOM元素拖拽功能的方法;主要分为两种;

  • 一是通过监听鼠标mouse事件
  • 二是使用Html5的拖拽的API drag & drop

二、使用原生js实现拖拽功能

使用该方法时,被拖拽的元素必须为绝对定位;
主要监听鼠标的三个事件:mousedown, onmousemove, onmouseup

// html结构
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <style>
  .box {
    position: absolute;
    background: blue;
  }
  </style>
</head>
<body>
    <div class="box" style="left:0;top:0;width:100px;height:100px">
    </div>
</body>
</html>

// js方法
<script>
    let dragDiv = document.getElementsByClassName("box")[0];
    // 鼠标按下事件 处理程序
    let putDown = function (event) {
      dragDiv.style.cursor = "pointer";
      let offsetX = parseInt(dragDiv.style.left); // 获取当前的x轴距离
      let offsetY = parseInt(dragDiv.style.top); // 获取当前的y轴距离
      let innerX = event.clientX - offsetX; // 获取鼠标在方块内的x轴距
      let innerY = event.clientY - offsetY; // 获取鼠标在方块内的y轴距
      // 按住鼠标时为div添加一个border
      dragDiv.style.borderStyle = "solid";
      dragDiv.style.borderColor = "red";
      dragDiv.style.borderWidth = "3px";
      // 鼠标移动的时候不停的修改div的left和top值
      document.onmousemove = function (event) {
          dragDiv.style.left = event.clientX - innerX + "px";
          dragDiv.style.top = event.clientY - innerY + "px";
          // 边界判断
          if (parseInt(dragDiv.style.left) <= 0) {
              dragDiv.style.left = "0px";
          }
          if (parseInt(dragDiv.style.top) <= 0) {
              dragDiv.style.top = "0px";
          }
          if (parseInt(dragDiv.style.left) >= window.innerWidth - parseInt(dragDiv.style.width)) {
              dragDiv.style.left = window.innerWidth - parseInt(dragDiv.style.width) + "px";
          }
          if (parseInt(dragDiv.style.top) >= window.innerHeight - parseInt(dragDiv.style.height)) {
              dragDiv.style.top = window.innerHeight - parseInt(dragDiv.style.height) + "px";
          }
      }
      // 鼠标抬起时,清除绑定在文档上的mousemove和mouseup事件
      // 否则鼠标抬起后还可以继续拖拽方块
      document.onmouseup = function () {
          document.onmousemove = null;
          document.onmouseup = null;
          // 清除border
          dragDiv.style.borderStyle = "";
          dragDiv.style.borderColor = "";
          dragDiv.style.borderWidth = "";
      }
  }
  // 绑定鼠标按下事件
  dragDiv.addEventListener("mousedown", putDown, false);
</script>


// 参考代码,封装拖拽函数

var funDrag = function(element, callback) {
    callback = callback || function() {};
    var params = {
        left: 0,
        top: 0,
        currentX: 0,
        currentY: 0,
        flag: false
    };
    //获取相关CSS属性
    var getCss = function(o,key){
        return o.currentStyle? o.currentStyle[key] : document.defaultView.getComputedStyle(o,false)[key];     
    };

    //拖拽的实现
    if(getCss(element, "left") !== "auto"){
        params.left = getCss(element, "left");
    }
    if(getCss(element, "top") !== "auto"){
        params.top = getCss(element, "top");
    }
    //o是移动对象
    element.onmousedown = function(event){
        params.flag = true;
        event = event || window.event;
        params.currentX = event.clientX;
        params.currentY = event.clientY;
    };
    document.onmouseup = function(){
        params.flag = false;    
        if(getCss(element, "left") !== "auto"){
            params.left = getCss(element, "left");
        }
        if(getCss(element, "top") !== "auto"){
            params.top = getCss(element, "top");
        }
        callback();
    };
    document.onmousemove = function(event){
        event = event || window.event;
        if(params.flag){
            var nowX = event.clientX, nowY = event.clientY;
            var disX = nowX - params.currentX, disY = nowY - params.currentY;
            element.style.left = parseInt(params.left) + disX + "px";
            element.style.top = parseInt(params.top) + disY + "px";
        }
    }    
};

三、使用Html5的特性实现

HTML 的 drag & drop 使用了 DOM event model 以及从mouse events 继承而来的 drag events 。一个典型的drag操作是这样开始的:用户用鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。 在操作期间,会触发一些事件类型,有一些事件类型可能会被多次触发(比如drag 和 dragover 事件类型)

相关重点:
1.DataTransfer 对象:退拽对象用来传递的媒介,使用一般为Event.dataTransfer。
2.draggable 属性:就是标签元素要设置draggable=true,否则不会有效果,例如:

<div title="拖拽我" draggable="true">列表1</div>

3.ondragstart事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上
4.ondragenter事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上
5.ondragover事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上
6.ondrop事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上
7.ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上
8.Event.preventDefault()方法:阻止默认的些事件方法等执行。在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发。另外,如果是从其他应用软件或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用用document的ondragover事件把它直接干掉。
9.Event.effectAllowed 属性:就是拖拽的效果。

// html结构
<div class="dustbin"><br />垃<br />圾<br />箱</div>
<div class="dragbox">
    <div class="draglist" title="拖拽我" draggable="true">列表1</div>
    <div class="draglist" title="拖拽我" draggable="true">列表2</div>
    <div class="draglist" title="拖拽我" draggable="true">列表3</div>
    <div class="draglist" title="拖拽我" draggable="true">列表4</div>
    <div class="draglist" title="拖拽我" draggable="true">列表5</div>
    <div class="draglist" title="拖拽我" draggable="true">列表6</div>
</div>
<div class="dragremind"></div>

// js代码
var eleDustbin = $(".dustbin")[0], eleDrags = $(".draglist"), lDrags = eleDrags.length, eleRemind = $(".dragremind")[0], eleDrag = null;
for (var i=0; i<lDrags; i+=1) {
    eleDrags[i].onselectstart = function() {
        return false;
    };
    eleDrags[i].ondragstart = function(ev) {
        /*拖拽开始*/
        //拖拽效果
        ev.dataTransfer.effectAllowed = "move";
        ev.dataTransfer.setData("text", ev.target.innerHTML);
        ev.dataTransfer.setDragImage(ev.target, 0, 0);
        eleDrag = ev.target;
        return true;
    };
    eleDrags[i].ondragend = function(ev) {
        /*拖拽结束*/
        ev.dataTransfer.clearData("text");
        eleDrag = null;
        return false
    };
}
eleDustbin.ondragover = function(ev) {
    /*拖拽元素在目标元素头上移动的时候*/
    ev.preventDefault();
    return true;
};

eleDustbin.ondragenter = function(ev) {
    /*拖拽元素进入目标元素头上的时候*/
    this.style.color = "#ffffff";
    return true;
};
eleDustbin.ondrop = function(ev) {
    /*拖拽元素进入目标元素头上,同时鼠标松开的时候*/
    if (eleDrag) {
        eleRemind.innerHTML = '<strong>"' + eleDrag.innerHTML + '"</strong>被扔进了垃圾箱';
        eleDrag.parentNode.removeChild(eleDrag);
    }
    this.style.color = "#000000";
    return false;
};