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

JavaScript访问未创建元素及Etch-a-Sketch颜色修改故障排查

Etch-a-Sketch项目:网格单元格颜色修改问题及解决

我正在完成TheOdinProject的Etch-a-Sketch项目,想要修改网格单元格的颜色,但这些单元格是在函数内部的for循环中创建的。搜索相关问题没找到合适的解决方案,或者看不懂相关内容。

创建网格的原函数代码

function changeGridSize(number) {
  let container = document.getElementById(`container`);
  totalNumber = number * number;
  while (container.hasChildNodes()) {
    container.removeChild(container.firstChild);
  }

  for (let i = 0; i < totalNumber; i++) {
    let gridSquare = document.createElement("div");
    gridSquare.classList.add("grid_square");

    let cellwidth = container.clientWidth / number + `px`;
    let cellheight = container.clientHeight / number + `px`;

    gridSquare.setAttribute(
      `style`,
      `width: ${cellwidth}; height: ${cellheight};`
    );
    container.appendChild(gridSquare);
  }
}

修改颜色的原代码

let color = document.getElementById(`colour_picker`);
color.addEventListener(`change`, changeColor() );
let choosenColour = color.value;

function changeColor() {
  while (container.hasChildNodes()) {
    gridSquare.setAttribute(`style`, `background-color: ${choosenColour};`)
  }
}

问题现象

预期效果是用户选择颜色后,网格单元格背景色改为所选颜色,但添加change事件监听器后页面崩溃,且网格尺寸调整到40x40以上时也会崩溃。

补充HTML代码

<div id="outer_container">
    <div id="container"> </div>
</div>
<div id="controls">
 <input type="color" class="user_input" name="" id="colour_picker" value="#000000">
<div class="slider">
        <div class="toggle_change" id="arrow_down"></div>
        <input style="margin-bottom: 0;" class="user_input" type="range" min="2" max="100" value="16" name="options"
            id="grid_range">
        <div class="toggle_change" id="arrow_up"></div>
    </div>

</div>

问题分析与修复方案

1. 事件监听的致命错误

原代码里的color.addEventListener('change', changeColor());直接调用了changeColor函数,页面加载时就会执行这个函数,而函数里的while (container.hasChildNodes())是无限循环——因为你只修改单元格样式,没有移除节点,hasChildNodes()永远为true,直接导致页面卡死崩溃。

修复:
把事件监听改成传函数引用,去掉括号:

color.addEventListener('change', changeColor);

2. 变量作用域与遍历逻辑错误

  • container和gridSquare是changeGridSize里的局部变量,changeColor函数根本访问不到。
  • choosenColour只在页面初始化时获取了一次颜色值,用户后续选择新颜色时不会更新。
  • 用while循环遍历单元格的逻辑完全错误,应该直接获取所有网格单元格元素并逐个修改样式。

修复后的changeColor函数:

let color = document.getElementById('colour_picker');
color.addEventListener('change', changeColor);

function changeColor() {
  const choosenColour = color.value; // 每次触发事件时获取最新选中的颜色
  const container = document.getElementById('container');
  const gridSquares = container.querySelectorAll('.grid_square'); // 获取所有带grid_square类的单元格

  gridSquares.forEach(square => {
    // 不要用setAttribute覆盖整个style,避免冲掉宽高属性,直接修改backgroundColor
    square.style.backgroundColor = choosenColour;
  });
}

3. 大网格性能优化

当网格尺寸调大(比如40x40=1600个单元格)时,原代码每次循环都直接向DOM添加节点,频繁触发浏览器重绘重排,导致页面卡顿甚至崩溃。

修复:
使用DocumentFragment批量添加节点,减少DOM操作次数:

function changeGridSize(number) {
  const container = document.getElementById('container');
  const totalNumber = number * number;
  container.innerHTML = ''; // 替代while循环移除子节点,更简洁高效

  const fragment = document.createDocumentFragment(); // 创建文档片段,暂存所有单元格
  const cellWidth = `${container.clientWidth / number}px`;
  const cellHeight = `${container.clientHeight / number}px`;

  for (let i = 0; i < totalNumber; i++) {
    const gridSquare = document.createElement('div');
    gridSquare.classList.add('grid_square');
    gridSquare.style.width = cellWidth;
    gridSquare.style.height = cellHeight;
    fragment.appendChild(gridSquare);
  }

  container.appendChild(fragment); // 一次性把所有单元格添加到容器,只触发一次DOM更新
}

另外,原代码里的totalNumber没有用let/const声明,会变成全局变量,可能引发其他问题,修复时加上了const。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 12:04:50