HTML5 拖放:不用第三方库,也能让元素“想拖就拖”的教程
54
0
0
核心就三句话:
- 给元素加
draggable="true"- 在
dragstart里把数据塞进“小书包”(dataTransfer)- 在目标区域阻止默认并取书包(
drop+preventDefault)
下面带你 15 分钟完成“卡片排序 + 图片上传”两个实战,代码复制就能跑。
1. 拖放流程 5 步图解(先背再敲)
| 步骤 | 触发事件 | 做什么 |
|---|---|---|
| ① 开始拖 | dragstart | 写数据 + 加视觉反馈 |
| ② 经过 | dragover | 必须 preventDefault(),否则无法落点 |
| ③ 进入 | dragenter | 高亮提示“可以松手” |
| ④ 放下 | drop | 读数据 + 改 DOM |
| ⑤ 结束 | dragend | 清场、去高亮 |
记忆口诀:“start 写,over 阻,enter 亮,drop 读,end 清”
2. 实战 1:Trello 式卡片排序
<ul id="list">
<li draggable="true">任务 A</li>
<li draggable="true">任务 B</li>
<li draggable="true">任务 C</li>
</ul>li { cursor: move; user-select: none; }
li.dragging { opacity: .5; }
li.dragover { border-top: 2px solid #007bff; }const list = document.getElementById('list');
let dragged = null;
/* === 拖拽源事件 === */
list.addEventListener('dragstart', e => {
if (e.target.tagName !== 'LI') return;
dragged = e.target;
e.target.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.target.innerHTML);
});
list.addEventListener('dragend', e => {
if (e.target.tagName === 'LI') e.target.classList.remove('dragging');
});
/* === 放置目标事件 === */
list.addEventListener('dragover', e => e.preventDefault()); // 必须阻默认
list.addEventListener('dragenter', e => {
if (e.target.tagName === 'LI') e.target.classList.add('dragover');
});
list.addEventListener('dragleave', e => {
if (e.target.tagName === 'LI') e.target.classList.remove('dragover');
});
list.addEventListener('drop', e => {
if (e.target.tagName === 'LI') {
e.target.classList.remove('dragover');
// 用 insertBefore 实现“插前面”排序
const after = e.target.nextSibling;
list.insertBefore(dragged, after);
}
});逻辑:把被拖元素插到“落点”前面,完成排序;无第三方库,移动端同样可用 。
3. 实战 2:拖拽图片 → 即刻预览 → 上传
<div id="dropBox" style="width:300px;height:200px;border:2px dashed #ccc;">
把图片拖到这儿
</div>
<ul id="preview"></ul>const dropBox = document.getElementById('dropBox');
// ① 阻止默认拖放行为,不然浏览器直接打开文件
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => {
dropBox.addEventListener(evt, e => e.preventDefault(), false);
});
// ② 进入/离开 视觉反馈
dropBox.addEventListener('dragenter', e => dropBox.style.background = '#eee');
dropBox.addEventListener('dragleave', e => dropBox.style.background = '');
// ③ 核心:drop 读取文件
dropBox.addEventListener('drop', e => {
dropBox.style.background = '';
const files = e.dataTransfer.files; // FileList 对象
[...files].filter(f => f.type.startsWith('image/')).forEach(file => {
const reader = new FileReader();
reader.onload = ev => {
const li = document.createElement('li');
li.innerHTML = `<img src="${ev.target.result}" width="150">`;
document.getElementById('preview').appendChild(li);
};
reader.readAsDataURL(file);
});
});关键:文件列表藏在dataTransfer.files,用它就能预览/上传,无需<input type="file">。
4. 数据传递“小书包”——dataTransfer 常用 API
| 方法 / 属性 | 说明 |
|---|---|
setData(format, data) | 写数据,格式任意字符串,常用 'text/html'、'text/plain' |
getData(format) | 读数据(只能在 drop 事件里读) |
clearData([format]) | 清数据 |
files | 文件列表(拖拽系统文件时) |
effectAllowed / dropEffect | 控制鼠标样式(copy/move/link/none) |
5. 事件触发顺序 & 坑位提醒
- 顺序固定:
dragstart→drag→dragenter→dragover→drop→dragend dragover每 100ms 触发一次,别在里面做重计算。- 想改拖拽“幽灵图”用
setDragImage(img, x, y);默认是元素快照。 - 如果拖的是链接或图片,浏览器默认会打开;记得
preventDefault()。
6. 兼容性 & 体积
- 拖拽 API 从 IE10 开始支持,移动端同样可用(手指长按即触发 drag)。
- 无需任何库,原生实现体积 = 0KB,性能拉满。
7. 一句话总结
“draggable 打开开关,dragstart 写数据,dragover 阻止默认,drop 读数据,dragend 清尾巴。”
背完这句,下次产品说“做个拖拽排序/上传”——直接甩原生代码,十分钟收工!
0
快来点个赞吧
发表评论
评论列表