写点什么

聊聊原生拖拽 API

作者:巨梦科技
  • 2023-05-19
    广东
  • 本文字数:2606 字

    阅读完需:约 9 分钟

拖拽 api 是我们前端非常常见的 api 了,比如拖拽排序,拖拽上传文件,树形结构的生成等等都会用到拖拽 api,拖拽 api 本身不复杂难得是在应用上,下面用一个例子来简单使用下这些 api


先实现布局如下


第一步:我们需要把左侧的元素变成可拖拽的,我们可以使用 html 属性给元素加一个draggable属性把它设为 true,该元素就可以拖拽了

<div class="container">  <ul class="drop-left">    <li draggable="true">vue</li>    <li draggable="true">react</li>    <li draggable="true">webpack</li>    <li draggable="true">node</li>    <li draggable="true">js</li>  </ul></div>
复制代码



html 的属性只是把它变成可拖拽元素,此时只能拖拽并没有效果,所以我们的通过书写 js 代码自定义一系列拖拽事件,拖拽其实就是一系列事件的组合

第二步:书写 js 代码监控元素拖拽和拖拽事件,我们可以通过 js 的事件委托直接监控他们的父元素来实现监控这个区域内所有和拖拽相关的事件了

const container = document.querySelector(".container");
// 此事件表示拖拽开始事件,可通过e.target拿到拖拽的元素container.ondragstart = (e) => { console.log("start", e.target);};
// 拖拽经过哪些元素上面就会触发该事件,可通过e.target知道我拖拽的元素目前在那个元素上面container.ondragover = (e) => { console.log("over", e.target);};
// 可以通过这个事件拿到当前元素拖到了哪个元素之上container.ondragenter = (e) => { console.log("enter", e.target);};
// 通过这个事件可以知道当前拖拽的元素是在那个元素落下的container.ondrop = (e) => { console.log("drop", e.target);};
复制代码


注意:像 div,td,li,table 等元素都是不允许别的东西元素拖拽到他上边的,所以不会触发 drop 事件,这是浏览器的默认行为,所以我们的可以在 over 事件中阻止默认行为


container.ondragover = (e) => {  e.preventDefault();};
复制代码


至此拖拽的事件已经写完了

第三步:修改拖拽元素默认自带的加号


我们可以通过 e.dataTransfer.effectAllowed 修改拖拽元素的状态


container.ondragstart = (e) => {  e.dataTransfer.effectAllowed = "move";};
复制代码



但是我们的动态设置状态,比如说从左边拖到右侧是 copy,从右侧拖动到左侧是 move,所以我们还是可以通过自定义属性来控制,如下我么后续可通过data-dropstatus来获取状态。


<div class="container">  <ul data-dropstatus="move" class="drop-left">    <li data-effect="copy" draggable="true">vue</li>    <li data-effect="copy" draggable="true">react</li>    <li data-effect="copy" draggable="true">webpack</li>    <li data-effect="copy" draggable="true">node</li>    <li data-effect="copy" draggable="true">js</li>  </ul>  <div class="drop-container">    <div data-dropstatus="copy"></div>    <div data-dropstatus="copy"></div>    <div data-dropstatus="copy"></div>    <div data-dropstatus="copy"></div>  </div></div>
container.ondragstart = (e) => { e.dataTransfer.effectAllowed = e.target.dataset.effect;};
复制代码

第四步:改变右侧容器当左侧拖进来时的背景色

container.ondragenter = (e) => {  e.target.classList.add("hover-background");};
复制代码


但这么些会有问题,enter 事件不仅会在子元素出发也会在父元素出发,所以整个 container 的背景色都会变。所以我们的修改为哪个元素背景色变是取决于我当前元素能拖拽到那个地方,能拖拽的地方背景色变不能拖拽的地方不能变,所以修改为如下


function getDropNode(node) {  while (node) {    if (node.dataset?.dropstatus) {      return node;    }    node = node.parentNode;  }}container.ondragenter = (e) => {  const dropNode = getDropNode(e.target);  if (    dropNode &&    dropNode.dataset.dropstatus === e.dataTransfer.effectAllowed  ) {    e.target.classList.add("hover-background");  }};
复制代码


此时会有个小问题就是经过的地方背景色都会变所以的清除下背景色



function clearClass() {  document.querySelectorAll(".hover-background").forEach((item) => {    item.classList.remove("hover-background");  });}
container.ondragenter = (e) => { clearClass(); const dropNode = getDropNode(e.target); if ( dropNode && dropNode.dataset.dropstatus === e.dataTransfer.effectAllowed ) { e.target.classList.add("hover-background"); }};
复制代码

第五步:处理 drop 放下事件 首先判断是复制元素还是删除元素,如果是是 copy 那么直接使用 start 事件中拖动的原始元素

let source;
container.ondragstart = (e) => { e.dataTransfer.effectAllowed = e.target.dataset.effect; source = e.target;};
container.ondrop = (e) => { clearClass(); const dropNode = getDropNode(e.target); if ( dropNode && dropNode.dataset.dropstatus === e.dataTransfer.effectAllowed ) { if (dropNode.dataset.dropstatus === "copy") { dropNode.innerHTML = ""; const cloned = source.cloneNode(true); cloned.dataset.effect = "move"; dropNode.appendChild(cloned); } }};
复制代码


如果是删除元素则直接 remove 就可


container.ondrop = (e) => {  clearClass();  const dropNode = getDropNode(e.target);  if (    dropNode &&    dropNode.dataset.dropstatus === e.dataTransfer.effectAllowed  ) {    if (dropNode.dataset.dropstatus === "copy") {      dropNode.innerHTML = "";      const cloned = source.cloneNode(true);      cloned.dataset.effect = "move";      dropNode.appendChild(cloned);    } else {      source.remove();    }  }};
复制代码


以上就是一个拖拽 demo 的实现,虽然拖拽能做的功能很多但也都是基于这几个 api 事件实现,我们可以在里面扩展各种别的功能,像拖拽布局,上下拖拽,自定义布局等等功能。

结尾

随着现在技术的不断发展,用户对页面的要求也越来越高了,这时候就衍生出了自定义布局页面内容和组件,我们的 Django-Vue-Admin 刚好实现了自定义首页功能,欢迎过来一起交流和探讨



官网链接: Django-Vue-Admin


QQ 群二维码,欢迎进群交流



用户头像

巨梦科技

关注

还未添加个人签名 2023-05-05 加入

一直想做一款后台管理系统,看了很多优秀的开源项目,发现只有Java、Go,但是发现没有合适Python的。于是利用空闲休息时间开始自己写一 套后台系统。如此有了Django-Vue-Admin管理系统。

评论

发布
暂无评论
聊聊原生拖拽API_django_巨梦科技_InfoQ写作社区