我正在参加「掘金·启航计划」
去年换工作之后,到手第一个负责的模块就是类低代码平台,研究了一下市面上的拖拽库,没有特别符合需求的,所以就打算自己梭哈一个,就去研究了一下h5的拖拽api,在这个过程中也遇到了一些坑,因为文章太长,所以分为上下两篇,第一篇就先讲解一下拖拽api的时候,第二篇讲解下我遇到的哪些坑,解决方案是什么
下面是我开发出来的一小部分成果
废话不多说,开始进入学习时间!
1 添加拖拽特性
dom
元素想要拖拽,只需要给dom
绑定一个draggrable
属性,就可以让这个dom
添加上拖拽特性
<style>#box {width: 100px;height: 100px;background-color: red;}</style><div id="box" draggable="true"></div><style> #box { width: 100px; height: 100px; background-color: red; } </style> <div id="box" draggable="true"></div><style> #box { width: 100px; height: 100px; background-color: red; } </style> <div id="box" draggable="true"></div>
2 拖拽事件
2.1 入门使用
h5
对于可拖拽的元素,提供了下面三个事件用于拖拽监听
- dragstart: 拖拽开始
- drag: 拖拽中
- dragend: 拖拽结束
<div id="box" draggable="true"></div><script>box.addEventListener('dragstart', function(e){console.log('拖拽开始')})box.addEventListener('drag', function(e){console.log('拖拽中')})box.addEventListener('dragend', function(e){console.log('拖拽结束')})</script><div id="box" draggable="true"></div> <script> box.addEventListener('dragstart', function(e){ console.log('拖拽开始') }) box.addEventListener('drag', function(e){ console.log('拖拽中') }) box.addEventListener('dragend', function(e){ console.log('拖拽结束') }) </script><div id="box" draggable="true"></div> <script> box.addEventListener('dragstart', function(e){ console.log('拖拽开始') }) box.addEventListener('drag', function(e){ console.log('拖拽中') }) box.addEventListener('dragend', function(e){ console.log('拖拽结束') }) </script>
2.2 DragEvent
当触发拖拽事件时候,会得到一个Event
对象,这个Event
对象其中包含这一些常用的属性
- target: 为拖拽中的元素的指向
- clientX: 当前鼠标的x点坐标
- clientY: 当前鼠标的y点坐标
- dataTransfer: 当一次拖拽开始发生的时候,会产生一个
DataTransfer
对象,这个DataTransfer
对象于整个拖拽过程触发的事件共享。当这一次的拖拽行为结束之后,这个对象会被销毁
2.3 DataTransfer
存放的数据只能在
dragstart
中存放,存放之后,在drag
和dragend
中拿不到
这个对象用于保存在 DragEvent
下的 dataTransfer
中,可以用来存放拖拽过程中产生的一些数据
2.3.1 存放数据
存放数据有三个api
- setData: 设置
data
- getData: 获取之前设置的
data
- clearData: 清除所有
data
需要注意的是,存放数据传入的值,都会调用 toString
方法,转化为字符串
// 存e.dataTransfer.setData('a', 1)e.dataTransfer.setData('b', '2')e.dataTransfer.setData('c', { a: '1', b: '2' })// 取e.dataTransfer.getData('a') // '1'e.dataTransfer.getData('b') // '2'e.dataTransfer.getData('c') // [ Object object ]// 存 e.dataTransfer.setData('a', 1) e.dataTransfer.setData('b', '2') e.dataTransfer.setData('c', { a: '1', b: '2' }) // 取 e.dataTransfer.getData('a') // '1' e.dataTransfer.getData('b') // '2' e.dataTransfer.getData('c') // [ Object object ]// 存 e.dataTransfer.setData('a', 1) e.dataTransfer.setData('b', '2') e.dataTransfer.setData('c', { a: '1', b: '2' }) // 取 e.dataTransfer.getData('a') // '1' e.dataTransfer.getData('b') // '2' e.dataTransfer.getData('c') // [ Object object ]
2.3.2 types
当存放了三个数据之后
e.dataTransfer.setData('a', 1)e.dataTransfer.setData('b', '2')e.dataTransfer.setData('c', { a: '1', b: '2' })e.dataTransfer.setData('a', 1) e.dataTransfer.setData('b', '2') e.dataTransfer.setData('c', { a: '1', b: '2' })e.dataTransfer.setData('a', 1) e.dataTransfer.setData('b', '2') e.dataTransfer.setData('c', { a: '1', b: '2' })
访问types
能返回存放的key
e.dataTransfer.types // [ 'a', 'b', 'c' ]e.dataTransfer.types // [ 'a', 'b', 'c' ]e.dataTransfer.types // [ 'a', 'b', 'c' ]
2.4 设置拖拽图片
在拖拽发生的时候,会产生一个降低一点透明度的源dom
的图片位于鼠标中心
我们可以单独设置拖拽显示的图片,比如下面的图标
const image = new Image()image.src = './drag.jpg'dragBox.addEventListener('dragstart', function (e) {e.dataTransfer.setDragImage(image, 50, 50)})const image = new Image() image.src = './drag.jpg' dragBox.addEventListener('dragstart', function (e) { e.dataTransfer.setDragImage(image, 50, 50) })const image = new Image() image.src = './drag.jpg' dragBox.addEventListener('dragstart', function (e) { e.dataTransfer.setDragImage(image, 50, 50) })
3 拖拽范围事件
当有个可以拖拽的元素之后,元素在拖拽的过程中,会经过一些元素,这里给他取一个形象一点的名称:过程元素
可以给这些过程元素绑定三个事件
- dragenter: 拖拽元素进入该
dom
时触发 - dragover: 拖拽元素在该
dom
范围内触发 - dragleave: 拖拽元素离开该
dom
时触发
<div class="process">过程元素</div><div class="process">过程元素</div><div class="process">过程元素</div>
const process = document.querySelector('.process')process.addEventListener('dragenter', function(e){ console.log('拖拽元素进入')})process.addEventListener('dragover', function(e){ console.log('拖拽元素移动')})process.addEventListener('dragleave', function(e){ console.log('拖拽元素离开')})const process = document.querySelector('.process') process.addEventListener('dragenter', function(e){ console.log('拖拽元素进入')}) process.addEventListener('dragover', function(e){ console.log('拖拽元素移动')}) process.addEventListener('dragleave', function(e){ console.log('拖拽元素离开')})const process = document.querySelector('.process') process.addEventListener('dragenter', function(e){ console.log('拖拽元素进入')}) process.addEventListener('dragover', function(e){ console.log('拖拽元素移动')}) process.addEventListener('dragleave', function(e){ console.log('拖拽元素离开')})
需要注意的是,这三个事件中,并不能拿到dragStart
中设置的DataTransfer
对象数据
4 放置事件
4.1 入门使用
当有可以拖拽的元素之后,总是需要有一个用于放置的地方,这个地方取一个形象的名称叫做目标元素。可以给目标元素绑定一个事件
- drop: 拖拽元素松开的位置在目标元素中,会触发这个函数【在这个函数中可以拿到在
dragStart
中存储的DataTransfer
】
准备一个例子
<style>.target{display: inline-block;width: 200px;height: 200px;border: 1px dashed #000}</style><div class="target">目标元素</div><style> .target{ display: inline-block; width: 200px; height: 200px; border: 1px dashed #000 } </style> <div class="target">目标元素</div><style> .target{ display: inline-block; width: 200px; height: 200px; border: 1px dashed #000 } </style> <div class="target">目标元素</div>
给目标元素绑定 drop
事件
const target = document.querySelector('.target')target.addEventListener('drop', function (e) {console.log('松开')})const target = document.querySelector('.target') target.addEventListener('drop', function (e) { console.log('松开') })const target = document.querySelector('.target') target.addEventListener('drop', function (e) { console.log('松开') })
尝试拖拽之后,会发现,这个函数根本没有被触发,这是因为还需要给目标元素绑定一个事件
dragover
为拖拽元素进入到目标元素的时候,触发的函数。然后在这个函数中,执行阻止默认行为,表示允许拖拽元素放置在此元素中
这样,对应的drop
就会触发
target.addEventListener('dragover', function (e) {e.preventDefault()})target.addEventListener('dragover', function (e) { e.preventDefault() })target.addEventListener('dragover', function (e) { e.preventDefault() })
4.2 drop:DataTransfer
只有在drop
中能拿到dragStart
存放的DataTransfer
,这个参数中,还有一个files
字段,借助这个字段,能实现文件拖拽上传的功能
例子如下
<div class="target">目标元素</div><script>const target = document.querySelector('.target')target.ondragover = function (e) {// 允许接受dom的放置e.preventDefault()}target.ondrop = function (e) {// 阻止默认行为,避免文件拖到浏览器会自动打开该文件e.preventDefault()// 文件会存放在e.dataTransfer.files中console.log(e.dataTransfer.files[0])}</script><div class="target">目标元素</div> <script> const target = document.querySelector('.target') target.ondragover = function (e) { // 允许接受dom的放置 e.preventDefault() } target.ondrop = function (e) { // 阻止默认行为,避免文件拖到浏览器会自动打开该文件 e.preventDefault() // 文件会存放在e.dataTransfer.files中 console.log(e.dataTransfer.files[0]) } </script><div class="target">目标元素</div> <script> const target = document.querySelector('.target') target.ondragover = function (e) { // 允许接受dom的放置 e.preventDefault() } target.ondrop = function (e) { // 阻止默认行为,避免文件拖到浏览器会自动打开该文件 e.preventDefault() // 文件会存放在e.dataTransfer.files中 console.log(e.dataTransfer.files[0]) } </script>