Skip to content

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>

注意事项

  1. 请注意浏览器的兼容性,部分较新的属性可能不被所有浏览器支持
  2. 在生产环境中使用前,建议进行充分的浏览器兼容性测试
  3. 某些属性的效果在不同浏览器中可能存在差异

最佳实践

  1. 在使用该属性前,先确认是否有更合适的语义化方案
  2. 参考规范文档确保正确使用
  3. 编写兼容性代码,为不支持该属性的浏览器提供回退方案

下一节

继续学习:contenteditable 可编辑

参考链接