- 
                Notifications
    
You must be signed in to change notification settings  - Fork 3
 
Description
HTML 拖拽
🌈 实现拖拽
实现拖拽需要定义拖拽元素和放置区域:
- 元素要支持可拖拽,需要做三件事:
- 设置 
draggable = true - 添加 
dragstart事件监听 - 在监听事件中设置拖拽数据
 
 - 设置 
 - 另一个元素要支持可放置:
- 监听 
dragover事件,事件中调用preventDefault()阻止默认事件,即可将该元素设置为可放置区域 - 监听 
drop事件,处理对应的逻辑 
 - 监听 
 
可拖拽元素定义好自己的拖拽数据
可放置元素定义要接受的数据和对应的处理逻辑
这样两个模块就是独立的模块,不会相互耦合。
🌈 API
❇️ draggable 属性
当我们想让元素变成可拖拽时,我们就需要设置 draggable 属性。
auto: 拖拽行为为浏览器默认行为,只有选中的文字,链接,图片可以拖动。【默认】true: 设置元素为可拖动。false: 设置元素为不可拖动
❇️ 拖拽事件
拖放过程的各个事件和触发时刻如下:
| 事件 | 触发时刻 | 
|---|---|
| dragstart | 当用户开始拖拽一个元素或选中的文本时触发 | 
| drag | 当拖拽元素或选中的文本时触发 | 
| dragend | 当拖拽操作结束时触发 (比如松开鼠标按键或敲“Esc”键) | 
| dragenter | 当拖拽元素或选中的文本到一个可放置的目标时触发 | 
| dragover | 当元素或选中的文本被拖到一个可放置的目标上时触发(每100毫秒触发一次) | 
| drop | 当元素或选中的文本在可放置的目标上被释放时触发 | 
| dragleave | 当拖拽元素或选中的文本离开一个可放置的目标时触发。 | 
| dragexit | 和dragleave类似,但是兼容性不好,建议不要使用。说明 | 
事件分类:
- 可拖拽元素:
dragstart,drag,dragend - 可放置的元素:
dragenter,dragover,drop,dragleave 
拖拽事件的 event 对象 dragEvent 继承 mouseEvent,dragEvent 有个属性 dataTransfer,dataTransfer 属性是一个 DataTransfer 对象。
接下来介绍这几个事件常用的方式:
- dragstart: 设置拖拽数据,调整拖拽元素样式等
 - drag:拖拽的移动跟踪
 - dragend:判断拖拽操作是否成功(即是否在放置区释放,通过 dropEffect 判断),恢复拖拽元素样式等
 - dragenter:设置样式,设置dropEffect等等
 - dragover:设置为可放置区,设置dropEffect等等
 - drop:处理放置事件
 - dragexit:恢复样式
 
<div id="drop-area"></div>
<div id="drag-el" draggable="true">...</div>
<div>放置结果:<span id="result"></span></div>
复制代码
window.onload = () => {
  const carEl = document.querySelector('#drag-el')
  const dropArea = document.querySelector('#drop-area')
  const result = document.querySelector('#result')
  carEl.addEventListener('dragstart', (event) => {
    event.dataTransfer.setData('text/plain', event.target.id)
    event.dataTransfer.effectAllowed = 'move'
    event.target.style.opacity = 0.5
  })
  carEl.addEventListener('drag', (event) => {
    console.log(event.clientX, event.clientY)
  })
  carEl.addEventListener('dragend', (event) => {
    event.target.style.opacity = 1
    const isDrop = event.dataTransfer.dropEffect !== 'none'
    result.innerText = isDrop ? '成功' : '未放置'
  })
  dropArea.addEventListener('dragenter', (event) => {
    event.dataTransfer.dropEffect = 'move'
    event.target.style.borderStyle = 'dashed'
  })
  dropArea.addEventListener('dragover', (event) => {
    event.dataTransfer.dropEffect = 'move'
    event.preventDefault()
  })
  dropArea.addEventListener('drop', (event) => {
    const id = event.dataTransfer.getData('text/plain')
    dropArea.appendChild(document.getElementById(id))
    event.target.style.borderStyle = 'solid'
  })
  dropArea.addEventListener('dragleave', (event) => {
    event.target.style.borderStyle = 'solid'
  })
}
复制代码上面这个例子中就使用到了各个事件,每个事件都有对应的用法。
❇️ 拖拽数据对象
拖拽数据对象涉及到三个类:DataTransfer, DataTransferItemList, DataTransferItem
DataTransfer
DataTransfer 对象用于保存在拖放操作期间拖动的数据,同时还可以设置拖拽样式,读取拖拽文件等等。它可以包含一个或多个数据项,每个数据项包含一个或多个数据类型。
所有拖拽事件中我们都可以通过 event.dataTransfer访问到它。
属性:
dropEffect:当前选定的拖放操作类型,一般在可放置元素的相关事件中设置effectAllowed:提供可用的操作类型,一般在拖拽元素的相关事件中设置files:包含数据传输中可用的所有本地文件的列表。如果拖动操作不涉及拖动文件,则此属性为空列表items:提供一个包含所有拖动数据列表的DataTransferItemList对象 ,只读属性。types:所有数据项类型的数组
方法:
clearData():删除与给定类型关联的数据。类型参数是可选的。如果类型为空或未指定,则删除与所有类型关联的数据setData():设置给定类型的数据。如果该类型的数据不存在,则将其添加到末尾,类型列表中的最后一项将是新的类型。如果该类型的数据已经存在,则在相同位置替换现有数据getData():获取给定类型的数据,如果该类型的数据不存在或 dataTransfer不包含数据,则返回空字符串setDragImage():用于设置自定义的拖动图像。
接下来我们将详细的介绍各个属性和方法。
dropEffect 和 effectAllowed
dropEffect 用于表示放置区接受什么行为的拖放,一般在 dragenter 和 dragover 中设置;对应的 effectAllowed 表示这次拖拽的行为是什么行为,要在 dragstart 中设置。
effectAllowed 支持的值:
none: 所有拖拽行为都允许copy: 支持复制行为move: 支持移动行为link: 支持链接关联行为copyMove: 支持 copy 和 movecopyLink: 支持 copy 和 linklinkMove: 支持 link 和 moveall: 支持 copy, move 和 linkuninitialized: 未设置值,默认和 all 效果一样
dropEffect 支持的值:
none: 不允许放置copy: 支持复制行为move: 支持移动行为link: 支持链接关联行为
这两个属性的作用有俩个:
- 鼠标样式会根据设置值展示,例如设置的是 move,在元素放置到拖拽区时,鼠标光标旁有个方形的样式
 dropEffect的值必须在effectAllowed范围中才可以支持放置,不然拖拽元素是无法在放置区中触发 drop 事件。
files
这个属性就如定义一样,如果拖拽的文件,这个数组就对应的就有 File 对象的数组项
items
这个属性保存了所有拖拽数据,我们在下面 DataTransferItemList 中介绍它。
types
这个数组保存了当前拖拽数据所有的数据类型值,我们可以看下默认图片拖拽时,这个数组的值情况:
可以看到图片拖拽的时候拖拽数据项有三个,它们的类型分别是 text/uri-list, text/html, Files。
这边我们顺便介绍下拖拽数据项的常见的类型值:text/plain, text/uri-list, text/html, Files,除了这些值,你自定义格类型值,如 application/x.bookmark。
clearData
DataTransfer.clearData([format]);这个方法就是清除拖拽数据项的方法,不传参数清除所有数据,有传就清除对应类型的数据。
setData
void dataTransfer.setData(format, data);
设置拖拽数据的方法,format 可以使用一些通用的值,如 text/plain,或者自定义值。data 为字符串,如果你数据是对象的话,需要序列化处理下。
同时设置数据默认会加到 dataTransfer.items 数组最后位置,但是如果数据的类型已存在,就会更新之前那条数据项的值。
getData
DOMString dataTransfer.getData(format);获取拖拽项数据的方法,传入数据类型获取。
setDragImage
void dataTransfer.setDragImage(img, xOffset, yOffset);
拖拽元素,浏览器默认会有一个图像。通过此方法可以自定义拖拽图像。
DataTransferItemList
一个 DataTransferItem 数组,DataTransferItem 代表的是一个拖拽数据项。event.dataTransfer.items 即是此类型
属性:
length: 数组长度
方法:
add(data, type)添加一个拖拽数据项,这个方法和 dataTransfer 的 getData 类似,不过在添加相同的类型数据的时候,这个方法会返回一个错误remove(index)移除一个数据项clear()清除数据项
DataTransferItem
拖拽时是数据项,event.dataTransfer.items 中的每一项即是此类型
属性:
kind: 指明数据是文件还是字符串,string | filetype: 数据的类型
方法:
getAsFile(callback)返回 File,拖拽不是文件就返回 nullgetAsFileSystemHandle(callback)返回文件或者文件夹 handle,拖拽不是文件就返回 nullgetAsString(callback)返回字符串
注意 DataTransferItem 获取数据方法是异步的,dataTransfer 的 getData 的获取数据是同步的。通常我们直接是使用 dataTransfer 的方法即可。
❇️ 兼容性
拖拽 API 主要还是在 PC 上使用,大部分移动设备都不支持。PC 端除了 ie 兼容性有些问题,其他浏览器兼容性良好。
🌈 总结
HTML 拖拽知识点中比较关键的有以下几点:
- 如何让元素支持拖拽
 - 如何让元素变成可放置区
 - 拖拽事件
 - 拖拽中的数据:dataTransfer 对象
 
掌握以上几个知识点,就能很好完成拖拽相关功能的编写。