HTML5 拖拽

225 阅读7分钟

HTML5提供专门的拖拽与拖放的API,以后实现这类效果就不必乱折腾了。但是,考虑到Opera浏览器似乎对此不感冒,在通用性上有待商榷,所以这里也就简单说一说。

兼容性: 不支持IE9

segmentfault.com/a/119000001… www.cnblogs.com/yehuisir/p/…

1. draggable 属性:就是标签元素要设置draggable=true,否则不会有效果,例如

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

拖放是 HTML5 中非常常见的功能。

注意: 为了让元素可拖动,需要使用 HTML5 draggable 属性。

提示: 链接和图片默认是可拖动的,不需要 draggable 属性。

在拖放的过程中会触发以下事件:

2. 在拖动目标上触发事件 (源元素) ,作用在被拖拽元素上:

ondragstart - 用户开始拖动元素时触发 ondrag - 元素正在拖动时触发 ondragend - 用户完成元素拖动后触发

3. 释放目标时触发的事件,作用在目标元素上:

  • ondragenter - 进入其容器范围内触发 当被鼠标拖动的对象进入其容器范围内时触发此事件
  • ondragover - 被拖动的对象在另一对象容器范围内拖动 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
  • ondragleave  - 拖动的对象离开其容器范围内时触发 当被鼠标拖动的对象离开其容器范围内时触发此事件
  • ondrop - 释放鼠标键时触发 在一个拖动过程中,释放鼠标键时触发此事件
<!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>拖动事件讲解</title>
    <style>
      .demo {
        list-style: none;
        background-color: aqua;
        height: 30px;
        width: 150px;
        margin-bottom: 20px;
      }
    </style>
  </head>
  <body>
    <!-- 图片和链接默认可以拖动  -->
    <!-- 被拖拽的源对象 -->
    <div class="demo" id="demo" draggable="true">测试</div>
​
    <!-- 目标对象,容器 -->
    <div
      id="wrapper"
      style="width: 300px; height: 400px; border: 1px solid #000"
    ></div>
​
    <script>
      //找到被拖拽的源对象
      var demo = document.getElementById("demo");
      // 目标对象,容器
      var wrapper = document.getElementById("wrapper");
​
      demo.ondragstart = function () {
        console.log("用户开始拖动元素的时候触发");
      };
      demo.ondrag = function () {
        console.log("正在拖动时候触发");
      };
      demo.ondragend = function () {
        console.log("结束拖动时候触发");
      };
​
      wrapper.addEventListener("dragenter", function () {
        console.log("dragenter 进入其容器范围内触发");
      });
      wrapper.addEventListener("dragover", function (ev) {
        ev.preventDefault();
        console.log("dragover 被拖动的对象在另一对象容器范围内拖动");
      });
      wrapper.addEventListener("dragleave", function () {
        console.log("dragleave 拖动的对象离开其容器范围内时触发");
      });
      wrapper.addEventListener("drop", function () {
        console.log("drop 释放鼠标键时触发");
      });
    </script>
  </body>
</html>

4. Event.preventDefault() 方法:

阻止默认的些事件方法等执行。在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发

另外,如果是从其他应用软件或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用用document的ondragover事件把它直接干掉。

DataTransfer 对象:拖拽对象用来传递的媒介,使用一般为Event.dataTransfer。

DataTransfer 对象用于保存拖动并放下(drag and drop)过程中的数据。它可以保存一项或多项数据,这些数据项可以是一种或者多种数据类型。 dataTransfer对象提供了一些方法用于在源元素与目标元素中共享数据 方法

  • setData(type,data):用于声明所发送的数据与类型 etData(type): 返回指定type的数据

    • clearData(type):删除指定类型的数据

event.dataTransfer.setDragImage(p_w_picpath,x,y);

  • setDragImage方法用于在拖放操作过程中,修改鼠标指针所指向的图像

5. 使用案例

  1. 通过拖拽分组
<!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>通过拖拽分组的小例子</title>
    <style>
      body {
        display: flex;
        justify-content: space-around;
      }
      .box1,
      .box2 {
        padding: 10px;
        width: 300px;
        border: 1px solid #000;
        min-height: 50px;
      }
      .box2 {
        max-height: 500px;
      }
      .demo {
        list-style: none;
        background-color: aqua;
        margin: 10px;
        height: 30px;
        padding-left: 5px;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div>
      <h1>组一:知识列表</h1>
      <ul class="box1" id="box1Out">
        <li class="demo" draggable="true">1. HTML5+CSS3</li>
        <li class="demo" draggable="true">2. WEBPACK</li>
        <li class="demo" draggable="true">3. VUE</li>
        <li class="demo" draggable="true">4. React</li>
        <li class="demo" draggable="true">5. ES5/ES6</li>
      </ul>
    </div>
    <div>
      <h1>组二:你喜欢的</h1>
      <ul class="box2" id="boxFrame"></ul>
    </div>
​
    <script>
      //被拖的对象
      var demolist = document.querySelectorAll(".demo");
      // 当前正在拖的是哪个li
      var dragDom = null;
      for (let i = 0, len = demolist.length; i < len; i++) {
        demolist[i].ondragstart = function (ev) {
          // console.log(this);
          // console.log(demolist[i]);
          dragDom = this;
        };
      }
​
      //目标对象, 容器
      var boxFrame = document.getElementById("boxFrame");
      // dragover  drap
​
      boxFrame.ondragover = function (ev) {
        ev.preventDefault();
      };
      boxFrame.ondrop = function () {
        console.log(dragDom);
        this.appendChild(dragDom);
        //被拖拽的对象插入到目标容器
      };
    </script>
  </body>
</html>
  1. 拖拽删除
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .dustbin {
        width: 100px;
        height: 260px;
        line-height: 1.4;
        background-color: gray;
        font-size: 36px;
        font-family: "微软雅黑", "Yahei Mono";
        text-align: center;
        text-shadow: -1px -1px #bbb;
        float: left;
      }
      .dragbox {
        width: 500px;
        padding-left: 20px;
        float: left;
      }
      .draglist {
        padding: 10px;
        margin-bottom: 5px;
        border: 2px dashed #ccc;
        background-color: #eee;
        cursor: move;
      }
​
      .dragremind {
        padding-top: 2em;
        clear: both;
      }
    </style>
  </head>
  <body>
    <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>
​
    <script>
      // 源对象, 被拖拽的对象
      var draglists = document.querySelectorAll(".draglist");
      //目标对象
      var dustbin = document.querySelector(".dustbin");
      var eleDom = null; //当前被拖拽的对象
      for (let i = 0, len = draglists.length; i < len; i++) {
        draglists[i].ondragstart = function (ev) {
          //开始拖拽
          ev.dataTransfer.setData("text", ev.target.innerHTML);
​
          var imgDom = new Image();
          imgDom.src = "./imgs/play-btn.png";
          ev.dataTransfer.setDragImage(ev.target, 0, 0);
          eleDom = this;
        };
        // 没有进到目标对象
        draglists[i].ondragend = function (ev) {
          //清除数据
          ev.dataTransfer.clearData("text");
          eleDom = null;
        };
​
        //目标对象
        dustbin.ondragenter = function (ev) {
          // 当被鼠标拖动的对象进入其容器范围内时触发此事件
          this.style.color = "#fff";
        };
​
        dustbin.ondragover = function (ev) {
          ev.preventDefault();
        };
        dustbin.ondrop = function (ev) {
          if (eleDom) {
            var info = ev.dataTransfer.getData("text");
            document.querySelector(".dragremind").innerHTML =
              info + "被扔进了垃圾箱";
            eleDom.remove();
            // eleDom.preventNode.removeChild(eleDom); //复杂
          }
        };
      }
    </script>
  </body>
</html>
  1. 拖拽上传文件
<!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>文件拖拽上传并以base64传给后台</title>
    <style>
      ul,
      li {
        list-style-type: none;
        margin: 0;
        padding: 0;
      }
      .out-main-top {
        height: auto;
        overflow: auto;
        display: flex;
        justify-content: flex-start;
      }
      .out-main-top button {
        padding: 10px 40px;
        font-weight: bold;
        font-size: 21px;
        height: 52px;
        margin-left: 30px;
        vertical-align: middle;
        margin-top: 60px;
      }
      .canvas-img {
        margin-top: 20px;
        clear: both;
      }
      .canvas-img li {
        width: 150px;
        height: 150px;
        position: relative;
        border: 1px solid #ccc;
        cursor: pointer;
        float: left;
        margin-right: 10px;
        overflow: hidden;
      }
​
      .canvas-img li img {
        width: 90%;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
      }
      .canvas-img li .close {
        position: absolute;
        right: 4px;
        top: 1px;
        font-style: normal;
        font-size: 12px;
        color: #666;
      }
​
      .drag-box {
        width: 250px;
        height: 200px;
        border: 1px dashed #ccc;
        margin-left: 20px;
        color: #ccc;
        font-size: 15px;
        text-align: center;
        padding-top: 90px;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <div class="out-main-top">
      <div id="dropbox" class="drag-box">或者将文件拖到此处</div>
      <button type="button" onclick="uploadFileNow()">上传</button>
    </div>
    <ul id="canvasImg" class="canvas-img"></ul>
​
    <script>
      var canvasImg = document.getElementById("canvasImg");
      //目标对象
      var dropbox = document.getElementById("dropbox");
      var allBaseImg = []; //需要给到后台的图片数据
      var AllowImgFileSize = 1024 * 400; //上传图片最大值(单位字节)超过400K上传失败
​
      dropbox.addEventListener("dragover", function (ev) {
        ev.preventDefault();
      });
      dropbox.addEventListener("drop", function (ev) {
        console.log(ev);
        // 通知web浏览器不要执行与事件关联的默认行为
        ev.preventDefault();
        console.log("files", ev.dataTransfer.files);
        var dtfiles = ev.dataTransfer.files;
        //转化成base64
        transferDataToBase64(dtfiles);
      });
​
      function transferDataToBase64(files) {
        for (let i = 0, len = files.length; i < len; i++) {
          var file = files[i];
          var reader = new FileReader();
          reader.readAsDataURL(file); //转化为base64的格式 ,异步
          reader.onload = function (ev) {
            var base64Img = ev.target.result;
            var index = allBaseImg.indexOf(base64Img); //includes
            if (index != -1) {
              return;
            }
            //图片大小是否符合
            if (base64Img.length > AllowImgFileSize) {
              alert("图片上传失败,需要小于400K");
              return;
            }
            var str = `<li> <img src='${base64Img}'> <i class='close'>X</i></li>`;
            canvasImg.innerHTML += str;
            allBaseImg.push(base64Img);
          };
        }
      }
​
      //监听缩略图 的删除事件 ,事件委托
      canvasImg.addEventListener("click", function (ev) {
        var target = ev.target;
        if (target.className == "close") {
          var thisbase = target.previousElementSibling; //方式一
          var sindex = allBaseImg.indexOf(thisbase);
          allBaseImg.splice(sindex, 1); //更新数据
          //删除缩略图
          target.parentElement.remove();
        }
      });
​
      //需要调接口,给图片数据到后端服务
      function uploadFileNow() {
        console.log(allBaseImg);
        console.log("调接口");
      }
    </script>
  </body>
</html>
  1. 拖拽排序
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <style>
    ul {
      list-style: none;
      margin: 200px;
      font-size: 0;
    }
    .ele {
      font-size: 16px;
      width: 100px;
      height: 40px;
      border: 1px solid #999;
      background: #ea6e59;
      margin: 2px 0;
      border-radius: 10px;
      padding-left: 10px;
      color: white;
      cursor: move;
    }
  </style>
  <body>
    <ul id="container">
      <li class="ele" draggable="true">1</li>
      <li class="ele" draggable="true">2</li>
      <li class="ele" draggable="true">3</li>
      <li class="ele" draggable="true">4</li>
      <li class="ele" draggable="true">5</li>
      <li class="ele" draggable="true">6</li>
      <li class="ele" draggable="true">7</li>
      <li class="ele" draggable="true">8</li>
    </ul>
  </body>
  <script>
    var node = document.querySelector("#container");
    var draging = null;
    //使用事件委托,将li的事件委托给ul
    node.ondragstart = function (event) {
      //firefox设置了setData后元素才能拖动!!!!
      event.dataTransfer.setData("te", event.target.innerText); //不能使用text,firefox会打开新tab
      draging = event.target;
    };
    node.ondragover = function (event) {
      event.preventDefault();
      var target = event.target;
      //因为dragover会发生在ul上,所以要判断是不是li
      if (target.nodeName === "LI" && target !== draging) {
        //_index是实现的获取index
        if (_index(draging) < _index(target)) {
          target.parentNode.insertBefore(draging, target.nextSibling);
        } else {
          target.parentNode.insertBefore(draging, target);
        }
      }
    };
​
    function _index(el) {
      var index = 0;
      if (!el || !el.parentNode) {
        return -1;
      }
      while (el && (el = el.previousElementSibling)) {
        index++;
      }
      return index;
    }
  </script>
</html>