React js原生 详解 HTML 拖放 API(鼠标拖放功能)

最近碰到了个需求,大概就是要通过可视化拖拽的方式配置一个冰柜,需要把预设好的冰柜内部架子模板一个个拖到冰箱内。一开始的想法是用鼠标事件(mousedown、mouseup等)那一套去实现,能实现但是过程过于复杂,需要控制的状态太多了。其实 Web Api 为 html 元素拖拽量身定制了一套 HTML 拖放API,用这个方法实现一些简单的拖拽功能简直不要太简单。为此写了这篇文章,下面将详细介绍 HTML 拖放 API 的核心知识点

一、被拖拽元素和放置被拖拽元素的元素

通常我们所了解的拖放是按住鼠标左键不放然后移动鼠标把一个页面元素从某个位置移动到另一个位置,然后松开鼠标左键,至此完成了整个拖放过程。在这个过程中我们需要先重点关注两个东西,一个是被拖拽元素另一个是 放置被拖拽元素的元素

1.1 被拖拽元素

我们得先有个概念,页面上显示的元素默认并不都是可以被拖拽的(除了图片、被选中的文字、链接),所以如果当前元素默认不可被拖拽那么就得先把它设置为可拖拽的。ps:可拖拽元素被拖拽时会有一个半透明的快照跟着鼠标移动。

将 HTML 元素的 draggable 属性设置为 true, 元素就可以变为可拖拽元素。效果如下图。

<div id="box" draggable="true">draggable box</div>

在这里插入图片描述

1.2 可放置被拖拽元素的元素

所有的元素区域默认是不支持放置被拖拽元素的,直观的表现是,当被拖拽元素经过不可放置区域时鼠标的样式是一个禁止放置的一个图标(圆圈带一个斜杠),所以需要将目标元素设置为一个可放置区域

默认情况下是这样:

<div id="box" draggable="true">draggable box</div>
<div id="droppable">放置区域</div>

在这里插入图片描述
设置为放置区域需要给元素绑定一个事件 dragover 且要 阻止默认行为

<div id="box" draggable="true">draggable box</div>
<div id="droppable">放置区域</div>
<script>let dropDom = document.getElementById('droppable')dropDom.addEventListener('dragover', (e) => {e.preventDefault()})
</script>

React:
放置区域需要绑定onDragOver事件,且要 阻止默认行为 – 其他事件一样加on

<div draggable="true">draggable box</div>
<div onDragOver={(e) => {e.preventDefault()}}>放置区域</div>

设置为可放置区域后鼠标样式也变了不再是禁止图标,而是一个加号图标(图标可以设置,下面会讲解):
在这里插入图片描述
然而你会发现被拖放元素并没有真正的被放置到放置区域,这是必然的,放置操作需要开发者自行定义,以上的设置只是是为了向用户表明这个区域是允许放东西的,那么至于怎么放需要开发者自行决定。

二、拖拽过程触发的一些事件

这一小节将带你了解整个拖放过程的其他细节,比如拖拽过程中会触发哪些事件

2.1 被拖放目标触发的事件

给被拖放目标元素绑定三个事件 dragstart、drag、dragend。

<div id="box" draggable="true">draggable box</div>
<div id="droppable">放置区域</div>
<script>let dragDom = document.getElementById('box')dragDom.addEventListener('dragstart', (e) => {console.log('开始拖动');})dragDom.addEventListener('drag', (e) => {console.log('拖动中');})dragDom.addEventListener('dragend', (e) => {console.log('结束拖动');})
</script>

React

<div  draggable="true"onDragStart={(e) => {console.log("开始拖动", e);}}onDrag={(e) => {console.log("拖动中", e);}}onDragEnd={(e) => {console.log("结束拖动", e);}}
>
>draggable box</div>
<div onDragOver={(e) => {e.preventDefault()}}>放置区域</div>

开始拖动触发 dragstart ,拖动过程中(鼠标不松开)触发drag,松开鼠标(或者按下 Esc 键)触发 dragend
在这里插入图片描述

2.2 被拖拽元素在放置区域内会触发的事件

先给放置目标元素绑定四个事件

<div id="box" draggable="true">draggable box</div>
<div id="droppable">放置区域</div>
<script>let dropDom = document.getElementById('droppable')dropDom.addEventListener('dragenter', (e) => {console.log('进入到了放置区域~');})dropDom.addEventListener('dragover', (e) => {e.preventDefault()console.log('在放置区域内拖拽中~');})dropDom.addEventListener('dragleave', (e) => {console.log('离开了放置区域~');})dropDom.addEventListener('drop', (e) => {console.log('在放置区域内,放下了被拖拽元素~')})
</script>

拖拽元素进入放置区域内时触发 dragenter 事件,在放置区域内移动被拖放(鼠标不松开)元素触发 dragover 事件,被拖放元素离开放置区域触发 dragleave 事件,在放置区域内松开鼠标触发 drop 事件。
在这里插入图片描述

三、实现真正意义上的元素拖放

通过上面触发的事件我们可以知道,用户真正在放置区域释放鼠标的时候只有 drop 事件能够监听到。所以开发者需要在这个事件里做真正的放置操作,放置什么由开发者决定,可以是被拖拽元素,也可以是自定义的一些内容。

放置被拖拽元素:

<div id="box" draggable="true">draggable box</div>
<div id="droppable">放置区域</div>
<script>let dropDom = document.getElementById('droppable')dropDom.addEventListener('dragover', (e) => {e.preventDefault()console.log('在放置区域内拖拽中~');})dropDom.addEventListener('drop', (e) => {console.log('在放置区域内,放下了被拖拽元素~')e.target.appendChild(document.getElementById('box'))})
</script>

在这里插入图片描述
放置自定义内容

dropDom.addEventListener('drop', (e) => {console.log('在放置区域内,放下了被拖拽元素~')let customCOntent = '<p>自定义内容</p>'e.target.innerHTML = e.target.innerHTML + customCOntent
})

在这里插入图片描述

四、dataTransfer 对象

4.1 从被拖放元素向可放置元素传递数据

dataTransfer 对象提供了一个setData()方法,它接受两个参数,第一个参数是传递数据的类型(一般是标准的MIME类型),第二个数据是数据值。dataTransfer 还提供了 getData() 的方法用于获取传递的数据,它接受一个参数,参数值为 setData 对应的第一个参数。

传递一个简单的字符串数据

<div id="box" draggable="true">draggable box</div>
<div id="droppable">放置区域</div>
<script>let dropDom = document.getElementById('droppable')let dragDom = document.getElementById('box')dragDom.addEventListener('dragstart', (e) => {e.dataTransfer.setData('text/plain', '自定义数据')})dropDom.addEventListener('dragover', (e) => {e.preventDefault()})dropDom.addEventListener('drop', (e) => {let data = e.dataTransfer.getData('text/plain')console.log('你传递的数据为:', data);})
</script>

在这里插入图片描述
⚡注意:只能在 dragstart 事件中设置数据,在其他地方设置无效。且只能在 drop 事件中获取设置的数据,其他事件中获取不到。
案例:根据传递的数据放置不同的内容。

<div id="box" draggable="true">draggable box</div>
<div id="droppable">放置区域</div>
<script>let dropDom = document.getElementById('droppable')let dragDom = document.getElementById('box')dropDom.addEventListener('dragover', (e) => {e.preventDefault()})dropDom.addEventListener('drop', (e) => {let num = e.dataTransfer.getData('num')console.log(num);if(num > 5)e.target.innerHTML = e.target.innerHTML + '<p>传递的数字大于5</p>'else if(num == 5) e.target.innerHTML = e.target.innerHTML + '<p>传递的数字等于5</p>'elsee.target.innerHTML = e.target.innerHTML + '<p>传递的数字小于5</p>'})dragDom.addEventListener('dragstart', (e) => {let num = Math.floor(Math.random() * 10) + 1;e.dataTransfer.setData('num', num)})
</script>

在这里插入图片描述

4.2 自定义拖拽过程中跟随鼠标移动的内容

默认情况下元素被拖拽时会有一个半透明的元素快照跟随着鼠标移动。通过 dataTransfer 提供的 setDragImage(elemnt, xOffset, yOffset) 方法是可以自定义跟随内容。接受三个参数 elemnt 可以是 dom 节点或者一个图片对象,xOffset, yOffset 是相对于鼠标的偏移量。

设置为一个图片:

<script>let dragDom = document.getElementById('box')dragDom.addEventListener('dragstart', (e) => {let img = new Image()img.src = './ikunlogo.png'e.dataTransfer.setDragImage(img, 10, 10)})
</script>

在这里插入图片描述

4.3 设置放置前的反馈图标

dataTransfer 提供了一个 dropEffect属性设置放置前的反馈图标,它有四种取值 none move copy link

在 dragover 中设置 dropEffect 的值

dropDom.addEventListener('dragover', (e) => {e.preventDefault()e.dataTransfer.dropEffect = 'link' // none || move || copy || link
})
  • 值为 none 或者经过不可放置区域,显示禁止放置图标在这里插入图片描述
  • 值为 move 时
    在这里插入图片描述
  • 值为 copy 时
    在这里插入图片描述
  • 值为 link 时
    在这里插入图片描述

4.4 拖动文件上传

通过 dataTransfer 的 files 属性可以获取用户拖拽的文件信息

拖拽系统文件到放置区域,并打印拖拽的文件信息:

dropDom.addEventListener('drop', (e) => {e.preventDefault()// 上传的文件列表let fileList = e.dataTransfer.filesfor (let i = 0; i < fileList.length; i++) {const file = fileList[i];console.log('文件名:' + file.name);console.log('文件大小:' + file.size);// 后续操作 比如:调接口上传文件}
})

在这里插入图片描述

4.5 清除 setData() 的值

dataTransfer 提供了 clearData() 清除 setData 设置的值,传参数则删除指定类型的值,不传则全部清除。

dropDom.addEventListener('drop', (e) => {console.log(e.dataTransfer.getData('text/plain'));console.log(e.dataTransfer.getData('text/html'));
})dragDom.addEventListener('dragstart', (e) => {e.dataTransfer.setData('text/plain', '自定义数据')e.dataTransfer.setData('text/html', '自定义数据2')e.dataTransfer.clearData('text/html')
})

在这里插入图片描述

4.6 查看设置了哪些类型的值

dataTransfer 提供了 types 属性查看 setData 设置了哪些类型的值。

dropDom.addEventListener('drop', (e) => {console.log(e.dataTransfer.types);
})
dragDom.addEventListener('dragstart', (e) => {e.dataTransfer.setData('text/plain', '自定义数据')e.dataTransfer.setData('text/html', '自定义数据2')
})

在这里插入图片描述

4.7 effectAllowed 属性取值会影响到 dropEffect 的取值效果。

effectAllowed 用于限制 dropEffect 只能设置哪些值

effectAllowed 的取值有: + none -> 此项表示 dropEffect 设置任何值都是禁止效果 + copy -> dropEffect 可以设置为 copy + copyLink -> dropEffect 可以设置为 copy 和 link + copyMove -> dropEffect 可以设置为 copy 和 Move + link -> dropEffect 可以设置为 link + linkMove -> dropEffect 可以设置为 link 和 Move + move -> dropEffect 可以设置为 Move + all -> dropEffect 可以设置为所有合法值 + uninitialized -> 等同 all 效果

dropDom.addEventListener('dragover', (e) => {e.preventDefault()e.dataTransfer.dropEffect = 'move'
})
dragDom.addEventListener('dragstart', (e) => {e.dataTransfer.effectAllowed = 'none'
})

上面即使 dropEffect 设置为 move, 但是 effectAllowed 的值为 none,所有还是禁止放置的反馈图标。
在这里插入图片描述

五、总结

  • 实现一个拖拽功能时先定义好被拖拽元素和放置区域元素。
  • 所有的放置操作都是在 drop 事件中完成。
  • 放置前的反馈效果可以根据你传递的数据来设置 dropEffect 显示不同的效果。
  • 被拖拽元素也可以是放置区域,放置区域也可以是被拖拽元素,两者没有明确的界限。
  • 功能自定义按需求开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101640.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

qt判断当前日期的当月的最后一天是几号

1、拖个dateTimeEdit在界面上&#xff0c;同时来判断输入的时间的最后一天的日期是什么&#xff1f; int year,month;int monthArr[12]{31,28,31,30,31,30,31,31,30,31,30,31};QDateTime time ui->dateTimeEdit->dateTime();year time.toString("yyyy").toIn…

uniapp 显示icon异常

按照文档创建的uni-ui项目&#xff0c;仿照示例程序写的代码中icon显示异常 &#xe470; 异常情况&#xff1a; 正常情况&#xff1a; 通过比对代码发现&#xff0c;示例程序的App.vue中 有一个引用是问题的关键 正是因为多了这一个引用文件&#xff0c;图表的显示才能正常 …

3d tiles规范boundingVolume属性学习

3d tiles的瓦片&#xff08;Tiles&#xff09;包含一些属性&#xff0c;其中第一项是boundingVolume&#xff1b;下面学习boundingVolume&#xff1b; boundingVolume&#xff0c;这个翻译为边界范围框&#xff0c;如果直译为边界体积可能有问题&#xff0c;其实就是包围盒的意…

[Unity][VR]Passthrough2-创建一个基本的Passthrough应用

上一期我们对PassthroughXR项目做好了基本的项目设置,今天我们就开始构建一个基本的Passthrough应用。 我们还是从基本场景开始。先把默认的main camera删除。因为后续我们会引入OVR Rig对象,这个对象自带Camera用来实现VR视角。 在Project面板我们搜索OVR camera rig。看见…

[Mono Depth/3DOD]单目3D检测基础

1.数据增强 图像放缩和裁剪后&#xff0c;相机内参要做相应变化 import random def random_scale(image, calib, scale_range(0.8, 1.2)):scale random.uniform(*scale_range)width, height image.sizeimage image.resize((int(width * scale), int(height * scale)))cali…

10_10C++

X-mid #include <iostream> using namespace std; class Kun {//算术运算符friend const Kun operator(const Kun &k1,const Kun &k2);friend const Kun operator-(const Kun &k1,const Kun &k2);friend const Kun operator*(const Kun &k1,const Ku…

MFC扩展库BCGControlBar Pro v33.6 - 网格、报表控件功能升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v33.6已正式发布了&#xff0c;此版本包含了对图表组件的改进、带隐藏标签的单类功能区栏…

腾讯云 轻量云 上海 VPS 测评

description: 发布于 2023-07-05腾讯云 轻量云 上海 VPS 测评 腾讯云国内机非常稳定&#xff0c;一年用下来没有掉线丢包的情况。国内机适合与备案域名一起建站使用。带宽很小&#xff0c;图片资源使用CDN加速或海外机提供。 规格 CPU - 2核 内存 - 2GB 系统盘 - SSD云硬盘…

上海-华为全联接大会|竹云受邀参加华为云ROMAConnect行业生态联盟成立联合发布会

2023年9月22日&#xff0c;在上海举办的华为全联接大会上&#xff0c;竹云作为华为云全方位合作伙伴代表&#xff0c;受邀参加华为云ROMAConnect行业生态联盟成立联合发布会。华为云PaaS服务产品部副部长张甲磊以及联盟主要成员企业出席发布仪式&#xff0c;共同见证华为云ROMA…

Tabby All configured authentication methods failed

文章目录 重要序言错误原因tabby的连接设置 总结 重要序言 Tabby是一款美观耐用的软件&#xff0c;平常一直用来输入密码方法SSH公司服务器&#xff0c;后来为了另外一台服务器加了SSH私钥&#xff0c;之后Tabby SSH连接死活不成功&#xff0c;哎&#xff0c;折腾了好久&#…

Git分布式版本控制工具

Git分布式版本控制工具 一、概述 1.1开发中的实际场景 备份代码还原协同开发追溯问题代码的编写人和编写时间 1.2版本控制器的方式 集中式版本控制工具 版本库是集中存放在中央服务器的&#xff0c;团队里每个人工作时从中央服务器下载代码&#xff0c;是必须联网才能工作&…

Ubuntu编译安装colmap遇到的几个问题以及解决

总体安装过程已经很明白了&#xff0c;写的人很多了&#xff0c;我就不赘述了&#xff0c;可以参考这里或者其他博客。我主要记录几个我遇到的问题以及解决方法。 1、cmake报错&#xff1a;No CMAKE_CUDA_COMPILER could be found. 这个原因是没找到cuda和nvcc目录&#xff0…

解决Opencv dnn模块无法使用onnx模型的问题(将onnx的动态输入改成静态)

一、问题来源 最近做人脸识别项目&#xff0c;想只用OpenCV自带的人脸检测和识别模块实现&#xff0c;使用OpenCV传统方法&#xff1a;Haar级联分类器人脸检测LBPH算法人脸识别的教程已经有了&#xff0c;于是想着用OpenCV中的dnn模块来实现&#xff0c;dnn实现人脸检测也有&a…

Fisher辨别分析

问题要求 在UCI数据集上的Iris和Sonar数据上验证算法的有效性。训练和测试样本有三种方式&#xff08;三选一&#xff09;进行划分&#xff1a; &#xff08;一&#xff09; 将数据随机分训练和测试&#xff0c;多次平均求结果 &#xff08;二&#xff09;K折交叉验证 &…

【苍穹外卖 | 项目日记】第三天

前言&#xff1a; 今天状态不错&#xff0c;kuku就是写接口&#xff0c;很舒服 目录 前言&#xff1a; 今日完结任务&#xff1a; 今日收获&#xff1a; 杂项知识点&#xff1a; 总结&#xff1a; 今日完结任务&#xff1a; 实现了新增菜品接口实现了菜品分页查询接口实现…

如何基于先进视频技术,构建互联网视频监控安全管理平台解决方案

一、建设思路 依托互联网&#xff0c;建设一朵云&#xff0c;实现各类二三类视频资源统一接入&#xff0c;实现天网最后100米、10米、1米的全域覆盖。 依托人工智能与互联网技术&#xff0c;拓展视频资源在政府、社会面等多领域的全面应用&#xff1b;建设与运营模式并存&…

软件测试工具有什么作用?有哪些好用的测试工具推荐?

软件测试工具是现代软件测试中不可或缺的重要组成部分&#xff0c;指的是一系列在软件开发过程中使用的工具&#xff0c;用于帮助测试人员进行测试活动&#xff0c;提高测试效率&#xff0c;减少测试成本。选择并使用合适的软件测试工具&#xff0c;可提高软件质量和效率。 一…

WebRTC 系列(四、多人通话,H5、Android、iOS)

WebRTC 系列&#xff08;三、点对点通话&#xff0c;H5、Android、iOS&#xff09; 上一篇博客中&#xff0c;我们已经实现了点对点通话&#xff0c;即一对一通话&#xff0c;这一次就接着实现多人通话。多人通话的实现方式呢也有好几种方案&#xff0c;这里我简单介绍两种方案…

应用在SMPS中的GaN/氮化镓

开关模式电源&#xff08;Switch Mode Power Supply&#xff0c;简称SMPS&#xff09;&#xff0c;又称交换式电源、开关变换器&#xff0c;是一种高频化电能转换装置&#xff0c;是电源供应器的一种。其功能是将一个位准的电压&#xff0c;透过不同形式的架构转换为用户端所需…

【2023】M1/M2 Mac 导入Flac音频到Pr的终极解决方案

介绍 原作者链接&#xff1a;https://github.com/fnordware/AdobeOgg 很早之前就发现了这个插件&#xff0c;超级好用&#xff0c;在windows上完全没有问题&#xff0c;可惜移植到mac就不行了&#xff08;然后我给作者发了一个Issue&#xff0c;后来就有大佬把m1的编译出来了&…