拖拽交互问题:拖拽舰船时仅触发单个棋盘列dragover事件
拖拽舰船时棋盘多列高亮问题解决方案
问题描述
我实现了一个10×10的棋盘,以及3个1行3列的舰船。需求为:拖拽舰船悬停在棋盘上时,舰船覆盖的所有棋盘列均显示特定背景色。当前已为所有棋盘列绑定dragover事件,为舰船设置draggable="true",但存在两个问题:
- 拖拽舰船时仅单个棋盘列变色;
- 仅当舰船的某一列悬停在棋盘列上时才触发
dragover事件,例如点击舰船第三列拖拽时,只有该列覆盖的棋盘列才会变色。
现有代码
script.js
import dragandrop from "./dragAndDrop.js"; const board = document.querySelector(".board"); const shipsContainer = document.querySelector(".ships"); // 创建10行10列的棋盘 function renderBoard() { for (let i = 1; i <= 10; i++) { const row = document.createElement("div"); row.classList.add(`row-${i}`, "row"); board.appendChild(row); for (let j = 1; j <= 10; j++) { const column = document.createElement("div"); column.classList.add(`row-${i}-column-${j}`, "column"); row.appendChild(column); } } } // 创建3个3格长的舰船 function renderShips() { for (let i = 1; i <= 3; i++) { const ship = document.createElement("div"); ship.classList.add(`ship-${i}`, "ship"); ship.setAttribute("draggable", "true"); shipsContainer.appendChild(ship); for (let j = 1; j <= 3; j++) { const shipColumn = document.createElement("div"); shipColumn.classList.add(`ship-column-${j}`, "ship-column"); ship.appendChild(shipColumn); } } } renderBoard(); renderShips(); dragandrop();
dragAndDrop.js
export default function dragandrop() { const columns = document.querySelectorAll(".column"); const ships = document.querySelectorAll(".ship"); ships.forEach((ship) => { ship.addEventListener("dragstart", (e) => { e.stopPropagation(); console.log("舰船开始拖拽"); }); }); columns.forEach((column) => { column.addEventListener("dragover", (e) => { column.style.backgroundColor = "red"; }); }); }
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>战舰拖拽</title> <!-- Google Fonts --> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet" /> <!-- Stylesheet --> <link rel="stylesheet" href="style.css" /> </head> <body> <div class="container"> <div class="board"></div> <div class="ships-container"> <div class="ships"></div> </div> </div> <script src="script.js" type="module"></script> </body> </html>
style.css
* { margin: 0; box-sizing: border-box; } body { height: 100vh; } .container { height: 100%; width: 100%; display: flex; justify-content: center; align-items: center; } .board { width: 400px; height: 400px; border: 1px solid black; overflow: hidden; } .row { width: 100%; height: calc(400px / 10); border-bottom: 1px solid black; display: flex; align-items: center; justify-content: center; } .row:last-of-type { border-bottom: none; } .column { height: 100%; width: calc(100% / 10); border-right: 1px solid black; } .column:last-of-type { border-right: none; } .ships-container { height: 400px; width: 200px; border: 1px solid black; margin-left: 2rem; display: flex; justify-content: center; align-items: center; } .ships { width: 100%; height: 76.5%; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 1rem; } .ship { width: min-content; height: auto; border: 1px solid black; display: flex; cursor: pointer; overflow: hidden; } .ship-column { width: 40px; height: 40px; border-right: 1px solid black; } .ship-column:last-of-type { border-right: none; }
解决方案
核心思路
- 将
dragover事件绑定到整个棋盘容器,而非单个列,确保拖拽过程中持续捕获位置信息 - 通过鼠标相对坐标计算舰船覆盖的棋盘列范围,批量设置背景色
- 处理拖拽结束和离开棋盘的事件,及时清除所有列的高亮状态
修改后的dragAndDrop.js代码
export default function dragandrop() { const board = document.querySelector(".board"); const columns = document.querySelectorAll(".column"); const ships = document.querySelectorAll(".ship"); const GRID_SIZE = 40; // 单个棋盘格的宽高,与CSS保持一致 const SHIP_LENGTH = 3; // 舰船长度 // 清除所有列的背景色 function clearHighlights() { columns.forEach(col => col.style.backgroundColor = ""); } ships.forEach((ship) => { ship.addEventListener("dragstart", (e) => { e.stopPropagation(); // 存储舰船长度,供后续计算使用 e.dataTransfer.setData("shipLength", SHIP_LENGTH); }); // 拖拽结束时清除高亮 ship.addEventListener("dragend", clearHighlights); }); // 监听棋盘的dragover事件 board.addEventListener("dragover", (e) => { e.preventDefault(); // 必须阻止默认行为才能触发后续拖拽逻辑 clearHighlights(); // 获取鼠标在棋盘内的相对坐标 const rect = board.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // 计算当前所在的行和起始列索引(从0开始) const rowIndex = Math.floor(y / GRID_SIZE); const startColIndex = Math.floor(x / GRID_SIZE); const shipLength = parseInt(e.dataTransfer.getData("shipLength")); // 计算结束列索引,避免超出棋盘范围 const endColIndex = Math.min(startColIndex + shipLength - 1, 9); // 高亮覆盖的所有列 for (let col = startColIndex; col <= endColIndex; col++) { const targetColumn = document.querySelector(`.row-${rowIndex + 1}-column-${col + 1}`); if (targetColumn) { targetColumn.style.backgroundColor = "red"; } } }); // 鼠标离开棋盘时清除高亮 board.addEventListener("dragleave", clearHighlights); }
关键调整说明
- 统一了棋盘格和舰船列的尺寸为40px,避免位置计算错位
- 将
dragover事件绑定到棋盘容器,确保拖拽过程中持续触发位置计算 - 通过鼠标相对坐标计算覆盖的列范围,批量设置背景色解决单格高亮问题
- 添加
dragend和dragleave事件处理,确保拖拽结束后清除所有高亮状态
内容的提问来源于stack exchange,提问作者UnkownSomeone
相关产品推荐
相关产品推荐

