You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript中16x16网格div鼠标进入事件绑定异常求助

解决网格div鼠标悬停仅最后一个元素变色的问题

嘿,我太懂你遇到的这个坑了——循环绑定事件的时候,所有事件好像都“记住”了最后一个div,不管悬停哪个,只有右下角的那个变色。这其实是JavaScript作用域和事件绑定的经典问题,咱们来一步步搞定它。

问题根源

大概率你的代码是类似这样的(用var声明循环变量,或者事件处理里直接引用了循环中的同一个变量):

const container = document.getElementById('grid-container');
// 循环创建16x16=256个div
for (var i = 0; i < 256; i++) {
  const square = document.createElement('div');
  square.classList.add('squares');
  container.appendChild(square);
  // 绑定鼠标进入事件
  square.addEventListener('mouseenter', function() {
    square.style.backgroundColor = 'white';
  });
}

这里的问题在于:当用var声明循环变量时,它是函数级作用域,而事件处理函数是异步触发的——等你真正把鼠标移上去的时候,循环早就跑完了,square变量已经指向了最后一个创建的div,所以所有事件都会修改它的样式。

三种解决方案

1. 用let创建块级作用域(最简洁)

把循环里的变量声明换成let,它会为每一次循环迭代创建一个独立的块级作用域,这样每个事件处理函数捕获的都是当前迭代的square变量:

const container = document.getElementById('grid-container');
for (let i = 0; i < 256; i++) {
  const square = document.createElement('div');
  square.classList.add('squares');
  container.appendChild(square);
  square.addEventListener('mouseenter', function() {
    // 这里用this也可以,this会指向触发事件的当前div
    this.style.backgroundColor = 'white';
    // 或者用square也没问题,因为let的作用域隔离了
    // square.style.backgroundColor = 'white';
  });
}

推荐用this,因为它更直接地指向当前触发事件的元素,逻辑更清晰。

2. 事件委托(更高效)

不用给每个div单独绑定事件,而是把事件绑定到它们的父容器上,利用事件冒泡机制来处理。这种方法在网格元素多的时候性能更好,而且后续新增div也不需要重新绑定事件:

const container = document.getElementById('grid-container');
// 先创建所有网格div
for (let i = 0; i < 256; i++) {
  const square = document.createElement('div');
  square.classList.add('squares');
  container.appendChild(square);
}
// 给父容器绑定一次事件
container.addEventListener('mouseenter', function(e) {
  // 判断触发事件的元素是我们的网格div
  if (e.target.classList.contains('squares')) {
    e.target.style.backgroundColor = 'white';
  }
});

这里e.target就是鼠标实际悬停的那个div,只要它的类名是squares,就修改它的背景色。

3. 闭包(兼容老版本JS)

如果你的环境不能用let,可以用立即执行函数(IIFE)创建闭包,把当前的div作为参数传递进去,让每个事件处理函数都持有当前div的引用:

const container = document.getElementById('grid-container');
for (var i = 0; i < 256; i++) {
  const square = document.createElement('div');
  square.classList.add('squares');
  container.appendChild(square);
  // 用IIFE包裹,把当前square传进去
  (function(currentSquare) {
    currentSquare.addEventListener('mouseenter', function() {
      currentSquare.style.backgroundColor = 'white';
    });
  })(square);
}

闭包会把currentSquare的引用保留下来,不会被后续循环覆盖。

总结

优先推荐第一种(let+this)或者第二种(事件委托)的写法,代码简洁易读,性能也更好。

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:19:26