为何重新渲染井字棋游戏面板后点击事件监听器失效?
井字棋游戏点击无响应问题解决
问题根源
- DOM元素引用失效:闭包内的
gameboardSpaces变量存储的是初始渲染时的DOM元素集合,当调用renderGameboard()清空gameboard的innerHTML后,这些元素会被从DOM树移除。后续调用selectSpace()时,是给这些已失效的元素绑定点击事件,完全无法作用于页面上的新格子。 - 事件绑定逻辑错误:原代码中
markSpace()调用selectSpace()时,绑定的还是旧元素的事件,新渲染的格子没有被绑定事件,导致后续点击无反应。
解决方案
推荐方案:事件委托
直接给游戏面板容器绑定点击事件,利用事件冒泡机制处理格子点击,无需每次渲染后重新给每个格子绑定事件,更高效且避免DOM引用失效问题。
修改后的完整代码(事件委托版)
const gameboardObject = (function () { let gameboardArray = ['', '', '', '', '', '', '', '', '']; function renderGameboard() { const gameboard = document.getElementById('gameboard'); gameboard.innerHTML = ''; for (let i = 0; i < gameboardArray.length; i++) { const space = document.createElement('div'); space.setAttribute('id', `boardspace-${i}`); space.setAttribute('class', 'boardSpaces'); space.textContent = gameboardArray[i]; gameboard.appendChild(space); } }; function setupEventListeners() { const gameboard = document.getElementById('gameboard'); // 移除旧监听防止重复绑定 gameboard.removeEventListener('click', handleBoardClick); gameboard.addEventListener('click', handleBoardClick); } function handleBoardClick(e) { // 确保点击的是格子元素 if (!e.target.classList.contains('boardSpaces')) return; const index = parseInt(e.target.id.split('-')[1]); // 跳过已被占据的格子 if (gameboardArray[index] !== '') return; markSpace(index); } function markSpace(index) { gameboardArray[index] = 'X'; renderGameboard(); }; // 初始化渲染和事件绑定 renderGameboard(); setupEventListeners(); return { gameboardArray, renderGameboard, markSpace, }; })();
关键修改说明
- 移除了闭包内的
gameboardSpaces变量,避免依赖失效的DOM引用。 - 使用事件委托将点击事件绑定到父容器
gameboard,无论格子如何重新渲染,点击事件都会被正确捕获。 - 在
handleBoardClick中增加了格子是否已被占据的判断,防止重复修改。 - 初始化时仅需调用一次
setupEventListeners(),无需每次渲染后重复绑定。
内容的提问来源于stack exchange,提问作者Daniel M.
相关产品推荐
相关产品推荐

