You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现Kanban看板时drop事件不触发?事件委托问题求助

问题描述

我正在尝试实现一个简易Kanban看板,为减少事件监听器数量,将dragstart、dragend、drop事件绑定到board元素而非单个task元素。目前dragstart和dragend事件正常工作,但drop事件无法触发,请问我哪里出错了?

另外,请问是否可以仅将事件监听器绑定到container div上?我尝试过但遇到了同样的drop事件不触发问题。


代码示例

HTML

<body>
<button id="add-task-btn">Add Task</button>
<div class="container">
    <!-- Todo Board -->
    <div id="todo-board" class="board">
    </div>

    <!-- In Progress Board  -->
    <div class="board">
    </div>

    <!-- Done Board -->
    <div class="board"></div>
</div>
</body>

CSS

* {
  box-sizing: border-box; 
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; 
} 

body { 
  margin: 0; 
} 

.container { 
  height: 80vh; 
  display: flex; 
  gap: 10px; 
  justify-content: center; 
  align-items: center; 
}

.board { 
  height: 80vh; 
  width: 300px; 
  border: 2px solid; 
  border-radius: 10px; 
  padding: 10px; 
} 

.task { 
  border: 2px solid; 
  padding: 5px 10px; 
  cursor: pointer; 
  background-color: blanchedalmond; 
  border-radius: 5px; 
}

JavaScript

const addTaskBtnEl = document.getElementById("add-task-btn");
const containerEl = document.querySelector(".container"); 
const boardEls = document.querySelectorAll(".board"); 
const todoBoardEl = document.getElementById("todo-board"); 
const state = { draggingTask: "" };

addTaskBtnEl.addEventListener("click", () => { 
  const taskItemText = prompt("Enter the task: "); 

  if (taskItemText) { 
    const p = document.createElement("p"); 
    p.classList.add("task"); 
    p.setAttribute("draggable", true); 

    const textNode = document.createTextNode(taskItemText); 
    p.appendChild(textNode); 

    todoBoardEl.appendChild(p); 
  } 
});

boardEls.forEach((el) => el.addEventListener("drop", handleDropTask)); 
boardEls.forEach((el) => el.addEventListener("dragstart", handleDragStartTask)); 
boardEls.forEach((el) => el.addEventListener("dragend", handleDragEndTask)); 

function handleDragStartTask(e) { 
  if (e.target.className === "task") {
    console.log("dragging start"); 
  } 
}

function handleDragEndTask(e) {
  console.log("dragging end");
}

function handleDropTask(e){ 
  e.preventDefault();
  console.log('dropped')
}

问题解答

1. Drop事件不触发的核心原因

浏览器默认禁止元素接受拖拽内容,要触发drop事件,必须先阻止目标元素**dragover事件的默认行为**。当拖拽元素经过board时,没有处理dragover事件,导致drop事件被浏览器拦截。

修复方案

给每个board添加dragover事件监听器,并补充拖拽元素的状态保存逻辑:

// 新增dragover事件处理,允许board接受拖拽内容
boardEls.forEach((el) => el.addEventListener("dragover", (e) => e.preventDefault()));

// 完善dragstart,保存当前拖拽的task元素
function handleDragStartTask(e) { 
  // 用classList.contains替代className判断,避免多类名场景失效
  if (e.target.classList.contains("task")) {
    state.draggingTask = e.target;
    console.log("dragging start"); 
  } 
}

// 完善drop事件,将task添加到目标board
function handleDropTask(e){ 
  e.preventDefault();
  if (state.draggingTask) {
    this.appendChild(state.draggingTask);
    state.draggingTask = "";
    console.log('dropped')
  }
}

2. 可以将事件监听器绑定到container上

完全可以利用事件委托,把所有拖拽事件绑定到container元素,同样需要处理dragover的默认行为,同时通过closest()方法定位目标board。

修改后的代码示例

// 移除原board上的事件绑定,改为绑定到container
containerEl.addEventListener("dragstart", handleDragStartTask);
containerEl.addEventListener("dragend", handleDragEndTask);
containerEl.addEventListener("dragover", (e) => {
  // 仅当拖拽经过board区域时,允许接受内容
  if (e.target.closest(".board")) {
    e.preventDefault();
  }
});
containerEl.addEventListener("drop", handleDropTask);

function handleDragStartTask(e) { 
  if (e.target.classList.contains("task")) {
    state.draggingTask = e.target;
    console.log("dragging start"); 
  } 
}

function handleDragEndTask(e) {
  state.draggingTask = "";
  console.log("dragging end");
}

function handleDropTask(e){ 
  e.preventDefault();
  // 找到当前drop操作对应的board元素
  const targetBoard = e.target.closest(".board");
  if (state.draggingTask && targetBoard) {
    targetBoard.appendChild(state.draggingTask);
    console.log('dropped')
  }
}

内容的提问来源于stack exchange,提问作者A. Ranjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 05:37:33