原生拖放
被拖拽元素
默认情况下,图像、链接和文本是可以拖动的,也就是说,图像和链接的 draggable 属性自动被设置成了 true,而其他元素这个属性的默认值都是 false。用户就可以拖动它们。文本只有在被选中的情况下才能拖动,而图像和链接在任何时候都可以拖动。
要想让其他元素可拖动,或者让图像或链接不能拖动,都可以设置这个属性。
<!-- 让这个图像不可以拖动 -->
<img src="smile.gif" draggable="false" alt="Smiley face" />
<!-- 让这个元素可以拖动 -->
<div draggable="true">...</div>拖动某元素时,将依次触发 下列事件: dragstart -> drag -> dragend
const dragElement = document.getElementById('dragElement')
function onDragStart(event) {
console.log(event.type, event)
}
function onDrag(event) {
event.preventDefault()
console.log(event.type, event)
}
function onDragEnd(event) {
console.log(event.type, event)
}
dragElement.addEventListener('dragstart', onDragStart, false)
dragElement.addEventListener('drag', onDrag, false)
dragElement.addEventListener('dragend', onDragEnd, false)- dragstart: 按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发 dragstart 事件。
- drag: 触发 dragstart 事件后,随即会触发 drag 事件,而且在元素被拖动期间会持续触发该事件。
- dragend: 当拖动停止时(无 论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发 dragend 事件。
目标元素
当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生: dragenter -> dragover -> dragleave 或 drop
dragenter: 只要有元素被拖动到放置目标上,就会触发dragenter事件(类似于 mouseover 事件)。dragover: 在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。dragleave或drop:如果元素被拖出了放置目标,dragover事件不再发生,但会触发dragleave事件(类似于 mouseout 事件)。如果元素被放到了放置目标中,则会触发drop事件而不是dragleave事件。
虽然所有元素都支持放置目标事件,但这些元素默认是不允许放置的。
如果拖动元素经过不允许放置的元素,无论用户如何操作,都不会发生 drop 事件。 不过,你可以把任何元素变成有效的放置 目标,方法是重写 dragenter 和 dragover 事件的默认行为。
const dropElement = document.querySelector('.drop')
// 进入可放置元素
function onDragEnter(event) {
event.preventDefault()
console.log(event.type, event)
}
// 在放置元素中移动 持续触发
function onDragOver(event) {
event.preventDefault()
// console.log(event.type, event);
}
// 移出可放置元素
function onDragLeave(event) {
event.preventDefault()
console.log(event.type, event)
}
// 在放置元素中停止拖拽
function onDrop(event) {
console.log(event.type, event)
}
dropElement.addEventListener('dragenter', onDragEnter, false)
dropElement.addEventListener('dragover', onDragOver, false)
dropElement.addEventListener('dragleave', onDragLeave, false)
dropElement.addEventListener('drop', onDrop, false)event.preventDefault()重写 dragenter 和 dragover 事件的默认行为,不然无论用户如何操作,都不会发生 drop 事件。
dataTransfer
除非数据受影响,否则简单的拖放并没有实际意义。 dataTransfer 对象可以为每种 MIME 类型都保存一个值。换句话说,同时在这个对象中保存一段文本和一个 URL 不会有任何问题。
DataTransfer 对象出现在拖拽事件中,具体包括开始拖拽 dragstart 事件,拖拽进入 dragenter 事件,拖拽离开 dragleave 事件,拖拽经过 dragover 事件,拖拽释放 drop 事件以及拖拽结束 dragend 事件,在拖放事件的事件处理程序外部无法访问 dataTransfer。
在从文本框拖动文本时,浏览器会调用 setData()并将拖动的文本以"text"格式存储起来。类似地,在拖动链接或图片时,浏览器会调用 setData()并把 URL 存储起来。当数据被放置在目标上时,可以使用 getData()获取这些数据。
function onDrop(event) {
// 拖拽文字
const text = event.dataTransfer.getData('text/plain')
// 拖拽链接
const url = event.dataTransfer.getData('text/uri-list')
// 获取拖拽富文本内容
const html = event.dataTransfer.getData('text/html')
}当然,可以在 dragstart 事件中手动调用 setData()存储自定义数据,以便将来使用。
/**
* 可以在 dragstart 事件处理程序中调用 setData()
* 手工保存自己要传输的数据,以便将来使用。
*/
function onDragStart(event) {
event.dataTransfer.setData('custom', JSON.stringify(data))
}
/**
* 保存在 dataTransfer 对象中的数据只能在 drop 事件处理程序中读取。
* 如果在 ondrop 处理程序中没有读到数据,那就是 dataTransfer 对象已经被销毁,数据也丢失了。
*/
function onDrop(event) {
// 自定义信息
const data = event.dataTransfer.getData('custom')
}DataTransfer.items 可以用来获取拖拽的数据信息。
function handleDrop(event) {
event.preventDefault()
const items = event.dataTransfer.items
for (const item of items) {
const { type, kind } = item
console.log(`kind: ${kind}, type: ${type}`)
if (kind === 'string') {
if (type.includes('text/plain')) {
item.getAsString((str) => {
// str是纯文本
})
}
if (type.includes('text/html')) {
item.getAsString((str) => {
// str是富文本
})
}
if (type.includes('text/uri-list')) {
item.getAsString((str) => {
// str是uri地址
})
}
}
// 文件类型
if (kind === 'file') {
// 如果是图片
if (type.includes('image/')) {
const file = items.getAsFile()
// file就是图片文件对象,可以上传,或者其他处理
}
}
}
}拖拽文件
用户通过拖入本地文件来上传文件到服务器,是个常见的使用场景
拖拽的本地文件列表。如果拖动操作不涉及拖动文件,则此属性为空列表。
<div class="container container-file">
<h4>拖拽文件到此区域</h4>
</div>const fileContainer = document.querySelector('.container.container-file')
fileContainer.addEventListener('drop', handleDragFile, false)
fileContainer.addEventListener('dragenter', handleDragFile, false)
fileContainer.addEventListener('dragover', handleDragFile, false)
function handleDragFile(event) {
event.preventDefault()
if (event.type === 'drop') {
// 遍历文件信息
const files = event.dataTransfer.files || []
console.log('files: ', files)
}
}拖拽排序
Reference
- DataTransfer 对象
- Sortable
- File接口提供有关文件的信息,并允许网页中的 JavaScript 访问其内容。