实现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
相关产品推荐
相关产品推荐

