元素拖拽
API 介紹
1. 拖放過程
整個拖放過程中,存在兩個關鍵元素:拖拽元素、放置元素 拖拽元素:被拖拽的元素
- drag:元素被拖拽時觸發,從開始拖拽到拖拽結束前整個過程會一直持續的觸發
- dragstart:元素被拖拽開始時觸發
- drop:拖拽元素被放置到放置元素內時觸發,如果沒有在放置元素內松手,則不會觸發
放置元素:
- dragenter:有拖拽元素進入時觸發
- dragover:有拖拽元素在該元素上時觸發,在離開前會持續觸發
- dragleave:拖拽元素離開時觸發
- dragend:拖拽元素放置時觸發
2. 可拖拽元素
在 HTML 中,文本、圖片和鏈接是默認可以拖放的元素。其他元素都是默認不可拖動的,如果需要讓其他非默認可拖動的 HTML 元素變得可拖動,比如<div>
、<span>
等,你需要明確地為這些元素設置 draggable="true"
屬性。這樣,這些元素就能夠接受拖放操作了。
3. 放置元素
所有 HTML 元素在默認情況下都不接受拖拽元素的放置,除非通過特定的事件處理來允許。
要使一個 HTML 元素能夠接受被拖動的元素,需要對這個元素進行一些特定的設置和事件綁定:
- dragover 事件:當被拖動的元素在另一個元素上方移動時,會觸發 dragover 事件。為了接受拖放,必須在 dragover 事件處理器中調用
event.preventDefault()
方法,這會阻止瀏覽器的默認行為,也就是不接受任何被拖放的元素。
4. DataTransfer
DataTransfer 對象用于保存拖動并放下(drag and drop)過程中的數據。它可以保存一項或多項數據,這些數據項可以是一種或者多種數據類型
- dropEffect:獲取當前選定的拖放操作類型,或設置為一個新的類型。值必須為 none、copy、link、move
- effectAllowed:提供所有可用的操作類型。值必須為 none、copy、copyLink、copyMove、link、copyMove、move、all、uninitialized
- files:包含數據傳輸中的所有本地文件列表
- items(只讀):提供一個包含所有拖動數據列表的 DataTransferItemList 對象
- types(只讀):一個提供 dragstart 事件中設置的格式的 strings 數組
<template><div id="drag-content" ref="contentRef" @dragstart="dragstart" @dragover="dragover" @dragenter="dragenter" @drop="drop" @dragend="dragend"><div class="left" data-drop="move"><div data-effect="copy" draggable="true" style="background: rgb(26, 231, 156)">語文</div><div data-effect="copy" draggable="true" style="background: rgb(107, 219, 15)">數學</div><div data-effect="copy" draggable="true" style="background: rgb(208, 133, 13)">英語</div><div data-effect="copy" draggable="true" style="background: rgb(30, 98, 246)">物理</div><div data-effect="copy" draggable="true" style="background: rgb(210, 40, 113)">化學</div><div data-effect="copy" draggable="true" style="background: rgb(210, 224, 26)">生物</div></div><div class="right"><table><thead><tr><td>星期一</td><td>星期二</td><td>星期三</td><td>星期四</td><td>星期五</td></tr></thead><tbody><tr><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td></tr><tr><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td></tr><tr><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td></tr><tr><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td><td data-drop="copy"></td></tr></tbody></table></div></div>
</template><script setup lang="ts">
import { reactive, ref } from 'vue'
const contentRef = ref(null)const state = reactive({source: null as HTMLElement | null,dropNode: null
})const dragover = (e) => {// 接受拖放元素e.preventDefault()
}const dragstart = (e) => {if (e.target.dataset.effect === 'move') {e.dataTransfer.effectAllowed = 'move'}state.source = e.target// 設置拖拽元素的樣式e.target.style.opacity = '0.2'
}const dragenter = (e) => {const dropNode = getDropNode(e.target)// 判斷放置元素是否可以接收拖拽元素,當 data-effect 和 data-drop 的值相等時,說明可以if (dropNode && dropNode.dataset.drop === (state.source as HTMLElement).dataset.effect) {// 給放置元素添加樣式dropNode.classList.add('hover-background')}
}// 獲取最近的可接受拖拽元素的父節點
const getDropNode = (node) => {while (node) {// 判斷元素是否設置了data-drop屬性,如果設置了,說明此元素是一個放置元素if (node.dataset && node.dataset.drop) {return node}node = node.parentNode}return node
}const clearHoverClass = () => {document.querySelectorAll('.hover-background').forEach((ele) => ele.classList.remove('hover-background'))
}const drop = (e) => {// 清除hover時的樣式clearHoverClass()// 獲取最近的放置節點const dropNode = getDropNode(e.target)if (dropNode && dropNode.dataset.drop === (state.source as HTMLElement).dataset.effect) {// 如果是新增課程if (dropNode.dataset.drop === 'copy') {dropNode.innerHTML = ''const cloned = (state.source as HTMLElement).cloneNode(true)if (cloned instanceof HTMLElement) {cloned.dataset.effect = 'move'cloned.style.opacity = '1'}dropNode.appendChild(cloned)// 移除課程} else {;(state.source as HTMLElement).remove()}}
}const dragend = (e) => {e.target.style.opacity = '1'
}
</script><style scoped lang="scss">
#drag-content {display: flex;.left {width: 80px;line-height: 32px;margin-right: 20px;div {padding: 10px;margin-bottom: 10px;text-align: center;color: white;}}.right {flex: 1;table {margin: 10px;td {width: 120px;height: 65px;div {padding: 10px;text-align: center;color: white;}}}}.hover-background {background-color: aqua;}
}
</style>