draggable 拖拽
draggable 全局属性指示元素是否可以被拖拽,配合 HTML5 Drag and Drop API 实现拖放交互。
前置知识
阅读本节前,建议先了解:tabindex 与 accesskey
基础概念
什么是draggable 拖拽
draggable 的三个值
| 值 | 说明 |
|---|---|
true | 元素可拖拽 |
false | 元素不可拖拽 |
auto | 使用浏览器默认行为 |
html
<!-- 可拖拽元素 -->
<div draggable="true" id="drag-item">拖拽我</div>
<!-- 拖放目标 -->
<div id="drop-zone" style="width:200px;height:200px;border:2px dashed #ccc;">放在这里</div>
<script>
const item = document.getElementById("drag-item");
const zone = document.getElementById("drop-zone");
item.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text", "拖拽的数据");
});
zone.addEventListener("drop", (e) => {
e.preventDefault();
const data = e.dataTransfer.getData("text");
zone.textContent = data;
});
zone.addEventListener("dragover", (e) => {
e.preventDefault(); // 允许放置
});
</script>语法与使用
基本语法
html
<div draggable="true">拖拽我</div>
<div draggable="false">不可拖拽</div>
<img draggable="false" src="image.jpg" alt="禁止拖拽的图片">拖放 API 事件
| 事件 | 目标 | 触发时机 |
|---|---|---|
dragstart | 拖拽元素 | 开始拖拽 |
drag | 拖拽元素 | 拖拽中 |
dragend | 拖拽元素 | 拖拽结束 |
dragenter | 放置目标 | 拖拽元素进入 |
dragover | 放置目标 | 拖拽元素悬停 |
dragleave | 放置目标 | 拖拽元素离开 |
drop | 放置目标 | 放下拖拽元素 |
详细说明
详细的说明和使用注意事项请参考 MDN 文档和实战示例。该属性在不同浏览器中的支持情况可能有所不同。
实战示例
拖放排序示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>拖放排序</title>
<style>
body { font-family: -apple-system, sans-serif; padding: 2rem; }
.list { list-style: none; padding: 0; }
.list li {
padding: 1rem; background: #f8fafc; border: 1px solid #e2e8f0;
margin-bottom: 0.5rem; border-radius: 6px; cursor: grab;
}
.list li.dragging { opacity: 0.5; }
.list li.drag-over { border-color: #3b82f6; background: #eff6ff; }
</style>
</head>
<body>
<h2>拖放排序</h2>
<ul class="list">
<li draggable="true">项目 1</li>
<li draggable="true">项目 2</li>
<li draggable="true">项目 3</li>
<li draggable="true">项目 4</li>
</ul>
<script>
const items = document.querySelectorAll(".list li");
let draggedItem = null;
items.forEach(item => {
item.addEventListener("dragstart", (e) => {
draggedItem = item;
item.classList.add("dragging");
});
item.addEventListener("dragend", () => {
item.classList.remove("dragging");
draggedItem = null;
});
item.addEventListener("dragover", (e) => {
e.preventDefault();
item.classList.add("drag-over");
});
item.addEventListener("dragleave", () => {
item.classList.remove("drag-over");
});
item.addEventListener("drop", (e) => {
e.preventDefault();
item.classList.remove("drag-over");
if (draggedItem !== item) {
const list = document.querySelector(".list");
const items = [...list.children];
const fromIdx = items.indexOf(draggedItem);
const toIdx = items.indexOf(item);
if (fromIdx < toIdx) {
list.insertBefore(draggedItem, item.nextSibling);
} else {
list.insertBefore(draggedItem, item);
}
}
});
});
</script>
</body>
</html>注意事项
- 请注意浏览器的兼容性,部分较新的属性可能不被所有浏览器支持
- 在生产环境中使用前,建议进行充分的浏览器兼容性测试
- 某些属性的效果在不同浏览器中可能存在差异
最佳实践
- 在使用该属性前,先确认是否有更合适的语义化方案
- 参考规范文档确保正确使用
- 编写兼容性代码,为不支持该属性的浏览器提供回退方案
下一节
继续学习:contenteditable 可编辑