首页 > 其他分享 >聊聊原生拖拽API

聊聊原生拖拽API

时间:2023-05-19 17:37:32浏览次数:64  
标签:container target 元素 dropNode API 聊聊 dataset 拖拽

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

先实现布局如下

image.png

第一步:我们需要把左侧的元素变成可拖拽的,我们可以使用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>

image.png

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();
};

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

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

image.png

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

container.ondragstart = (e) => {
  e.dataTransfer.effectAllowed = "move";
};

image.png

但是我们的动态设置状态,比如说从左边拖到右侧是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");
  }
};

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

image.png

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刚好实现了自定义首页功能,欢迎过来一起交流和探讨

image.png

官网链接: Django-Vue-Admin

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

image.png

标签:container,target,元素,dropNode,API,聊聊,dataset,拖拽
From: https://www.cnblogs.com/jumengkeji/p/17415809.html

相关文章

  • SpringBoot 配置统一API对象返回
    1、前言在实际项目开发中,为了便于前端进行响应处理,需要统一返回类格式。特别是在有多个后端开发人员参与的情况下,如果不规范返回类,每个人按照个人习惯返回数据,前端将面临各式各样的返回数据,难以统一处理。为解决这个问题,我们需要规范后端的返回数据,并定义一个统一的返回类,所有数......
  • web页面获取显示钉钉智能会议室申请信息,调用智能会议室api,并传参数
    首页获得会议室房间名称1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8">5<metahttp-equiv="X-UA-Compatible"content="IE=edge">6<metaname="......
  • API架构的选择,RESTful、GraphQL还是gRPC
    hi,我是熵减,见字如面。在现代的软件工程中,微服务或在客户端与服务端之间的信息传递的方式,比较常见的有三种架构设计的风格:RESTful、GraphQL和gRPC。每一种模式,都有其特点和合适的使用场景,今天,我们主要来对三种风格做一个深入的理解和对比,以方便我们在做技术选型时,能够做出有效的......
  • 聊聊Mybatis集成Spring的原理
    一般都是研究框架源码,我为什么要反过来研究集成原理呢?在我自己看来,集成虽然比较简单,但要求的细节比较多,需要掌握根本性的东西才能做到集成。Mybatis集成Spring用到了FactoryBean以及BeanDefinition注册的原理,从这两个维度来实现集成,而我们单独学习Spring时,一般会忽略这两点。My......
  • echart常用的几个api函数
    在对echart进行二次封装时,以下几个api函数很有用。首先是,init和dispose,我们在创建页面及页面卸载时可以使用,让echart的资源能在组件卸载时被释放。this.chart=echarts.init(this.$refs.echart);this.$once('hook:beforeDestroy',()=>{this.chart.dispose();})......
  • 亚马逊AMAZON中国站API详情接口获取商品详情接口
       亚马逊中国站是亚马逊在中国开设的在线购物平台,提供包括图书、电子产品、家居生活、服饰鞋包等各类品类的商品。亚马逊为了满足中国消费者的需求,特别推出了全球购和自营模式两种购物方式,全球购支持跨境购物,自营模式则是由亚马逊直接发货并提供售后服务。同时,亚马逊还提......
  • Apipost Error: Invalid URI "http:///%20"
    1.情景展示使用Apipost调接口时,出现如上图所示的错误。2.具体分析去他们官网,帮助文档,连个搜索功能都没有,也是服了。说明这个请求地址有问题,但实际我把请求地址放到浏览器当中是可以正常访问的。3.解决方案百思不得其解。最后,发现:原来,是自己在复制URL的时候,前面多了一个......
  • 【淘宝拼多多抖音】订单详情API接口系列
    订单详情接口主要是获取订单的详细信息,包括但不限于订单号、下单时间、支付状态、发货状态、收货人信息、商品信息、金额、物流信息、退货/换货信息等。这些数据可以用于订单管理和追踪订单状态,以提供更好的客户服务。搜索当前会话用户作为卖家已卖出的交易数据(只能获取到三个月......
  • NACOS 2.2.2 com.alibaba.nacos.api.exception.NacosException: user not found!
    因服务端设置了鉴权,nacos.core.auth.enabled=true(参照官方文档https://nacos.io/zh-cn/docs/auth.html)客户端需增加相关配置(username和password)spring:application:name:xxxxxcloud:nacos:server-addr:xxxxxxconfig:file-extensio......
  • 什么是 Angular 的 API Extractor?
    Angular的APIExtractor是一个用于生成和管理TypeScript库的API文档的工具。它的工作原理是通过分析TypeScript代码,并提取其中的公共API,生成清晰的文档以便开发者了解库的使用方式、函数、类、接口等。APIExtractor的工作流程如下:配置:首先,需要创建一个名为"api-e......