事件监听器疑似异常:Etch-A-Sketch网格随机变黑问题
问题分析与修复:Etch-A-Sketch 网格随机变黑
你的问题核心出在事件监听重复绑定和匿名函数无法移除这两个点上,具体原因和修复方案如下:
问题根源
- 重复绑定事件:每次调用
createGrid时,都会给resize、reset、eraseBtn、blackBtn以及grid重新绑定事件监听。比如点几次Reset后,reset按钮会绑定多个clearGrid回调,grid也会有多个mouseover监听,鼠标移动时多个回调同时触发,就会出现随机变黑的情况。 - 匿名函数无法移除:在
blackPaint和eraseColor里用了匿名箭头函数作为mouseover回调,后续调用removeEventListener时找不到对应的函数引用,导致旧的监听一直留在grid上,多个监听冲突。 - 目标元素判断不严谨:
colorBlack里直接用e.target.className !== "block"判断,要是元素有多个类名(比如后续加其他样式)就会失效,而且HTML里grid外还有个多余的.block元素,会干扰事件判断。
修复方案
1. 把按钮事件监听移到createGrid外,只绑定一次
按钮的点击逻辑不会随网格重建变化,没必要每次创建网格都绑定,直接在初始化时绑定一次即可。
2. 统一使用命名函数,避免匿名函数
把blackPaint和eraseColor里的匿名回调改成命名函数,这样才能正确移除旧的监听。
3. 修复事件目标判断逻辑
用element.classList.contains('block')代替直接判断className,同时删掉HTML里grid外多余的.block元素。
修改后的完整代码
JavaScript
let blackBtn = document.querySelector('#black'); let eraseBtn = document.querySelector('#eraser'); let resetBtn = document.querySelector('#reset'); let resize = document.querySelector('.resize'); let grid = document.querySelector('#grid'); // 补上缺失的grid变量 let value = 16; // 默认16x16网格 // 统一的黑色绘制函数 function colorBlack(e) { if (!e.target.classList.contains("block")) return; e.target.style.backgroundColor = 'black'; } // 统一的擦除函数 function eraseColorHandler(e) { if (!e.target.classList.contains("block")) return; e.target.style.backgroundColor = 'white'; } // 创建网格 function createGrid(size = value) { grid.style["grid-template-rows"] = `repeat(${size}, 1fr)`; grid.style["grid-template-columns"] = `repeat(${size}, 1fr)`; grid.innerHTML = ''; // 先清空旧网格,避免重复创建 for (let i = 0; i < size * size; i++) { const block = document.createElement('div'); block.classList.add('block'); grid.appendChild(block); } // 初始化时绑定默认的黑色绘制监听 grid.addEventListener("mouseover", colorBlack); } // 调整网格大小 function resizeGrid() { const newSize = parseInt(prompt("New Size: ", 16)); if (isNaN(newSize) || newSize < 1 || newSize > 100) { // 增加输入合法性判断 alert('请输入1-100之间的有效数字'); return; } grid.removeEventListener("mouseover", colorBlack); grid.removeEventListener("mouseover", eraseColorHandler); createGrid(newSize); } // 重置网格 function clearGrid() { grid.removeEventListener("mouseover", colorBlack); grid.removeEventListener("mouseover", eraseColorHandler); createGrid(); } // 切换到黑色绘制模式 function blackPaint() { grid.removeEventListener("mouseover", eraseColorHandler); grid.addEventListener("mouseover", colorBlack); } // 切换到擦除模式 function eraseColor() { grid.removeEventListener("mouseover", colorBlack); grid.addEventListener("mouseover", eraseColorHandler); } // 初始化事件绑定(只执行一次) resize.addEventListener("click", resizeGrid); resetBtn.addEventListener("click", clearGrid); eraseBtn.addEventListener("click", eraseColor); blackBtn.addEventListener("click", blackPaint); // 初始化创建网格 createGrid();
CSS(保留原代码)
* { padding: 0; margin: 0; box-sizing: border-box; } #grid { width: 20rem; height: 20rem; border: 1px solid #333; display: grid; flex-wrap: grid; } .block { border: 1px solid black; }
HTML(删除grid外的多余.block元素)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Etch-A-Sketch</title> <link rel="stylesheet" href="style.css"> </head> <body> <header> <p>Etch-A-Sketch</p> </header> <div class="gameBoard"> <div id="actions"> <button class="resize">Resize</button> <button id="reset">Reset</button> <button id="black">Color Black</button> <button id="eraser">Eraser</button> </div> <div id='grid'></div> </div> <script src="script.js"></script> </body> </html>
额外优化点
- 补上了原代码中缺失的
grid变量定义 - 给
resizeGrid增加了输入合法性判断,避免非法值导致网格异常 - 创建网格时先清空
grid.innerHTML,避免重复添加元素
内容的提问来源于stack exchange,提问作者Supablu
相关产品推荐
相关产品推荐

