Drag API 拖拽事件

HTML5 Drag API 的 7 个拖拽事件及使用方法

问题

HTML5 Drag API 有哪些拖拽相关的事件?它们分别在什么时候触发?

解答

Drag API 共有 7 个事件,分为两类:拖拽元素事件目标区域事件

事件分类

事件触发对象触发时机
dragstart被拖拽元素开始拖拽时
drag被拖拽元素拖拽过程中持续触发
dragend被拖拽元素拖拽结束时
dragenter目标元素拖拽元素进入目标区域时
dragover目标元素拖拽元素在目标区域上方时持续触发
dragleave目标元素拖拽元素离开目标区域时
drop目标元素在目标区域释放时

完整示例

<!DOCTYPE html>
<html>
<head>
  <style>
    .draggable {
      width: 100px;
      height: 100px;
      background: #3498db;
      color: white;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: move;
    }
    .dropzone {
      width: 300px;
      height: 200px;
      border: 2px dashed #ccc;
      margin-top: 20px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .dropzone.over {
      border-color: #3498db;
      background: #ecf0f1;
    }
  </style>
</head>
<body>
  <div class="draggable" draggable="true" id="box">拖拽我</div>
  <div class="dropzone" id="zone">放置区域</div>

  <script>
    const draggable = document.getElementById('box');
    const dropzone = document.getElementById('zone');

    // ========== 拖拽元素事件 ==========

    // 开始拖拽
    draggable.addEventListener('dragstart', (e) => {
      // 设置拖拽数据
      e.dataTransfer.setData('text/plain', e.target.id);
      // 设置拖拽效果
      e.dataTransfer.effectAllowed = 'move';
      console.log('dragstart: 开始拖拽');
    });

    // 拖拽过程中持续触发
    draggable.addEventListener('drag', (e) => {
      // 注意:这个事件触发频率很高,避免执行耗时操作
      console.log('drag: 拖拽中...');
    });

    // 拖拽结束
    draggable.addEventListener('dragend', (e) => {
      console.log('dragend: 拖拽结束');
    });

    // ========== 目标区域事件 ==========

    // 进入目标区域
    dropzone.addEventListener('dragenter', (e) => {
      e.preventDefault();
      dropzone.classList.add('over');
      console.log('dragenter: 进入目标区域');
    });

    // 在目标区域上方(必须阻止默认行为才能触发 drop)
    dropzone.addEventListener('dragover', (e) => {
      e.preventDefault(); // 关键:允许放置
      e.dataTransfer.dropEffect = 'move';
      console.log('dragover: 在目标区域上方');
    });

    // 离开目标区域
    dropzone.addEventListener('dragleave', (e) => {
      dropzone.classList.remove('over');
      console.log('dragleave: 离开目标区域');
    });

    // 放置
    dropzone.addEventListener('drop', (e) => {
      e.preventDefault();
      dropzone.classList.remove('over');
      
      // 获取拖拽数据
      const id = e.dataTransfer.getData('text/plain');
      const element = document.getElementById(id);
      
      // 将元素移动到目标区域
      dropzone.appendChild(element);
      console.log('drop: 放置完成');
    });
  </script>
</body>
</html>

dataTransfer 对象

dataTransfer 用于在拖拽过程中传递数据:

// 设置数据
e.dataTransfer.setData('text/plain', 'hello');
e.dataTransfer.setData('application/json', JSON.stringify({ id: 1 }));

// 获取数据(只能在 drop 事件中获取)
const text = e.dataTransfer.getData('text/plain');

// 设置拖拽效果
e.dataTransfer.effectAllowed = 'copy'; // copy, move, link, all
e.dataTransfer.dropEffect = 'copy';

// 设置拖拽图像
e.dataTransfer.setDragImage(img, offsetX, offsetY);

关键点

  • 元素需设置 draggable="true" 才能拖拽
  • 必须dragover 中调用 e.preventDefault() 才能触发 drop 事件
  • dataTransfer.getData() 只能在 drop 事件中调用
  • dragdragover 事件触发频率高,避免执行耗时操作
  • 事件触发顺序:dragstartdragdragenterdragoverdragleave/dropdragend