首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau
百百 BLOG
首頁
學習紀錄
遊戲心得影視Life書單案件檔案
Side Projects委託作品與二創互動實驗場
Kurau

Kurau Blog

「隨心而寫,真真假假,都是我」

一個記錄生活、輸出興趣的個人空間。
遊戲、影視、閱讀、學習……每一段體驗都值得留下文字。

頁面導覽

  • 學習紀錄
  • 遊戲心得
  • 影視Life
  • 書單
  • 委託作品與二創
  • Kurau
  • 合作邀請

找到我

歡迎來 Discord 找我聊天!

“曾經發生的事不可能忘記,只是暫時想不起來而已。”-《神隱少女》

© 2026 Kurau All rights reserved

前端基礎

拖曳事件 (DragEvent)

By Kurau·2023-03-08·Updated 2026-05-09·3 分鐘閱讀

拖曳事件(DragEvent / HTML5 Drag and Drop)

TL;DR
三個核心事件:dragstart(來源)、dragover(目標,必須 preventDefault())、drop(目標完成接收)。沒呼叫 preventDefault() 的話 drop 不會觸發,這是最常踩的雷。

三個必懂事件

事件觸發時機觸發在哪必做
dragstart使用者開始拖拉一個 draggable="true" 的元素來源元素e.dataTransfer.setData() 存資料
dragover拖拉中經過某元素的「上方持續觸發」目標元素必須 e.preventDefault(),否則 drop 不觸發
drop使用者放開滑鼠目標元素e.preventDefault() + e.dataTransfer.getData() 取資料

完整範例(原生 JS)

<ul id="list">
  <li draggable="true" data-index="0">A</li>
  <li draggable="true" data-index="1">B</li>
  <li draggable="true" data-index="2">C</li>
</ul>
html
const list = document.getElementById('list');

list.addEventListener('dragstart', (e) => {
  // 1. 標記哪個 item 被拖
  e.dataTransfer.setData('text/plain', e.target.dataset.index);
  e.dataTransfer.effectAllowed = 'move';
});

list.addEventListener('dragover', (e) => {
  e.preventDefault(); // ⚠️ 必須!否則 drop 不會觸發
  e.dataTransfer.dropEffect = 'move';
});

list.addEventListener('drop', (e) => {
  e.preventDefault();
  const fromIndex = parseInt(e.dataTransfer.getData('text/plain'));
  const toIndex = parseInt(e.target.dataset.index);
  // ...交換邏輯
});
javascript

React 版本

function SortableList({ items, onReorder }: Props) {
  const [draggedIndex, setDraggedIndex] = useState<number | null>(null);

  return (
    <ul>
      {items.map((item, i) => (
        <li
          key={item.id}
          draggable
          onDragStart={(e) => {
            setDraggedIndex(i);
            e.dataTransfer.effectAllowed = 'move';
          }}
          onDragOver={(e) => e.preventDefault()}
          onDrop={(e) => {
            e.preventDefault();
            if (draggedIndex !== null && draggedIndex !== i) {
              onReorder(draggedIndex, i);
            }
            setDraggedIndex(null);
          }}
        >
          {item.label}
        </li>
      ))}
    </ul>
  );
}
tsx
原始來源
Stack Overflow — Move items up in a list with click with reactjs

常見地雷

🪤 dragover 沒 preventDefault → drop 永遠不觸發

這是 80% 拖曳功能爆掉的原因。瀏覽器預設不允許 drop,要 listener 主動「我接受」才會觸發 drop 事件。

🪤 dataTransfer.setData('text/plain', ...) 必須是字串

不能直接傳 object,要先 JSON.stringify:

e.dataTransfer.setData('application/json', JSON.stringify(item));
// drop 時:
const item = JSON.parse(e.dataTransfer.getData('application/json'));
javascript

🪤 行動裝置(Touch)不會觸發 DragEvent

HTML5 Drag and Drop 只支援滑鼠,手機觸控完全不觸發。要支援手機:

  • 用 dnd-kit(React)/ Sortable.js(原生)等套件,內部會處理 Touch + Pointer events
  • 自己用 pointerdown / pointermove / pointerup 自訂

實務建議

用套件還是原生?
  • 簡單的列表排序:原生夠用,4-5 個 listener 就完成
  • 複雜場景(虛擬滾動、跨容器、多選、行動裝置):直接用套件,別自己寫
    • React: @dnd-kit/core、react-beautiful-dnd(已停更)
    • 原生: Sortable.js

目錄

    ◆ 相關文章

    • 監聽 passive 屬性

      2026-05-09
    • 滑鼠滾輪事件 (WheelEvent)

      2026-05-09
    • iframe 嵌入

      2026-05-09
    • SCSS 編譯 前綴問題

      2026-05-09
    ← 上一篇從現在開始改用Next.js下一篇 →監聽 passive 屬性

    ◆ 關於作者

    Kurau

    個人寫作 / 創作的 SoT,記錄遊戲、影視、學習與生活。

    更多 Kurau 的文章