,内嵌 Unicode 棋子符号(或 img 标签);
利用 HTML5 draggable="true" + dragstart / dragover / drop 事件实现拖放;
在 drop 时通过坐标计算目标格子索引,将棋子 DOM 节点移动至对应格子内部,实现“吸附”效果。
? 示例代码(精简可运行版)
<meta charset="UTF-8"><title>可拖拽国际象棋棋盘</title><style>
.chessboard {
display: grid;
grid-template-columns: repeat(8, 60px);
grid-template-rows: repeat(8, 60px);
width: 480px;
height: 480px;
margin: 20px auto;
border: 2px solid #333;
box-shadow: 0 0 12px rgba(0,0,0,0.2);
}
.cell {
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
user-select: none;
cursor: default;
}
.cell.light { background-color: #f0d9b5; }
.cell.dark { background-color: #b58863; }
.piece {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 42px;
cursor: grab;
z-index: 10;
transition: transform 0.15s ease;
}
.piece:active { cursor: grabbing; transform: scale(0.95); }
.piece.white { color: #222; }
.piece.black { color: #fff; text-shadow: 1px 1px 2px #000; }
</style><div class="chessboard" id="board"></div>
<script>
const board = document.getElementById('board');
const pieces = [
['♜', '♞', '♝', '♛', '♚', '♝', '♞', '♜'],
['♟', '♟', '♟', '♟', '♟', '♟', '♟', '♟'],
['', '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['', '', '', '', '', '', '', ''],
['♙', '♙', '♙', '♙', '♙', '♙', '♙', '♙'],
['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖']
];
// 渲染棋盘与初始棋子
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const cell = document.createElement('div');
cell.className = `cell ${(row + col) % 2 === 0 ? 'light' : 'dark'}`;
cell.dataset.row = row;
cell.dataset.col = col;
const pieceChar = pieces[row][col];
if (pieceChar) {
const piece = document.createElement('div');
piece.className = `piece ${row < 2 ? 'black' : 'white'}`;
piece.draggable = true;
piece.textContent = pieceChar;
piece.dataset.row = row;
piece.dataset.col = col;
piece.addEventListener('dragstart', handleDragStart);
cell.appendChild(piece);
}
board.appendChild(cell);
}
}
// 拖拽逻辑
function handleDragStart(e) {
e.dataTransfer.setData('text/plain', `${e.target.dataset.row},${e.target.dataset.col}`);
e.dataTransfer.effectAllowed = 'move';
}
board.addEventListener('dragover', e => e.preventDefault()); // 必须阻止默认行为才能触发 drop
board.addEventListener('drop', e => {
e.preventDefault();
const [srcRow, srcCol] = e.dataTransfer.getData('text/plain').split(',').map(Number);
const targetCell = e.target.closest('.cell');
if (!targetCell) return;
const dstRow = +targetCell.dataset.row;
const dstCol = +targetCell.dataset.col;
// 移动棋子:从原格移除,插入目标格
const piece = board.querySelector(`[data-row="${srcRow}"][data-col="${srcCol}"]`);
if (piece && targetCell.children.length === 0) {
targetCell.appendChild(piece);
piece.dataset.row = dstRow;
piece.dataset.col = dstCol;
// 可选:添加视觉反馈
targetCell.style.backgroundColor = '#aaffaa';
setTimeout(() => {
targetCell.style.backgroundColor = '';
}, 300);
}
});
</script>
⚠️ 关键注意事项
-
必须调用 e.preventDefault():在 dragover 事件中,否则 drop 事件不会触发;
-
避免嵌套 draggable 元素:确保 .piece 是直接子元素,且 .cell 不设 draggable="true";
-
坐标管理需同步更新:每次移动后务必更新 dataset.row/col,为后续合法走法校验打下基础;
-
移动端兼容性:原生 drag API 在 iOS/Android 上支持有限;如需全平台支持,建议改用 Pointer Events 或第三方库(如 interact.js);
-
无障碍友好:为 .piece 添加 role="img" 和 aria-label(如 aria-label="白方车"),提升可访问性。
✅ 后续扩展建议
- 引入棋子颜色标识与选中高亮(:focus-within 或 JS 状态管理);
- 添加“撤销上一步”与“重置棋盘”按钮;
- 封装为 Class(如 ChessBoard),支持初始化配置与事件钩子(onMove, onInvalidDrop);
- 结合 FEN 字符串解析,实现棋局持久化与加载。
该方案兼顾简洁性与可维护性,是构建专业级网页象棋应用的理想起点——拖拽即所见,吸附即所达。