JavaScript通过DOM操作动态生成黑白棋盘格

作者:微光

时间:2026-11-13

浏览:119

详细讲解如何使用JavaScript通过DOM操作动态生成黑白棋盘格,包括行列奇偶性计算、CSS Grid网格布局、DocumentFragment批量挂载、与innerHTML拼接方案的差异对比以及常见问题排查。

文章目录
JavaScript通过DOM操作动态生成黑白棋盘格
文章缩略图:JavaScript 通过计算行列坐标奇偶性创建 DOM 节点,并配合 CSS Grid 渲染黑白棋盘格

用 JavaScript 动态生成黑白棋盘格,核心在于两件事:用行列索引之和的奇偶性 (row + col) % 2 决定每个格子的颜色,再用 DOM 节点创建 API 配合 CSS Grid 把这些格子放进页面容器。下面按照“布局准备 → 分步实现 → 完整示例与验证 → 方案对比 → 常见问题排查”的顺序展开说明,读完可以直接在本地浏览器运行出一个支持自定义尺寸的黑白棋盘格。

一、准备页面容器与黑白交替规则

在写 JavaScript 之前,先明确页面结构与颜色规律。HTML 中只需要一个空的挂载容器,例如 <div id="chessboard" class="board"></div>,所有格子都交给脚本生成。

棋盘排版推荐使用 CSS Grid。把容器设为 display: grid,并通过 CSS 自定义属性 --board-size 控制列数(grid-template-columns: repeat(var(--board-size, 8), 1fr)),这样不需要在 DOM 里额外嵌套代表“行”的 <div>,所有格子作为容器的直接子节点就能自动按行折返。

颜色规律依靠二维坐标决定。假设棋盘有 size 行 size 列,行号 row 和列号 col 都从 0 开始计数。只要把 row + col 对 2 取余:

  • 当 (row + col) % 2 === 0 时,为浅色格子(添加 cell-light 类)。
  • 当 (row + col) % 2 === 1 时,为深色格子(添加 cell-dark 类)。

因为向左右移动一格时 col 变化 1,向上下移动一格时 row 变化 1,所以任意相邻格子的 row + col 奇偶性必然相反,从而保证横向、纵向都严格黑白交替。

JavaScript通过DOM操作动态生成黑白棋盘格
图 1:二维坐标奇偶性判定与单层循环序号直接取余的效果差异(以 4×4 偶数阶网格为例)

二、分步实现 DOM 动态生成棋盘格

把棋盘生成逻辑封装成函数 renderCheckerboard(container, size = 8),按下面四个步骤执行,每一步只做一件明确的事。

JavaScript通过DOM操作动态生成黑白棋盘格
图 2:JavaScript 动态生成棋盘格的四步执行流程

第 1 步:获取容器并校验尺寸参数

通过 document.getElementById("chessboard") 取得挂载点,确认节点存在且传入的 size 是大于 0 的整数,避免误传负数或非数字导致死循环或空白页面。随后用 container.style.setProperty("--board-size", String(size)) 把列数同步给 CSS Grid。

第 2 步:创建 DocumentFragment 暂存节点

调用 document.createDocumentFragment() 创建一个轻量级的文档片段。它是存在于内存中的节点容器,不属于当前页面 DOM 树,因此往里面追加几十甚至上千个格子时,不会反复触发表面的样式计算和布局重排。

第 3 步:双层循环创建单元格并赋予样式与坐标

外层循环变量 row 从 0 到 size - 1,内层循环变量 col 从 0 到 size - 1。每次迭代调用 document.createElement("div") 创建格子元素,根据 (row + col) % 2 === 0 决定设置 cell cell-light 还是 cell cell-dark。顺手把行列号写入 cell.dataset.row 和 cell.dataset.col,方便后续点击格子时直接读取位置,然后把节点放进 fragment。

第 4 步:使用 replaceChildren 一次性挂载到页面

循环结束后,调用 container.replaceChildren(fragment)。这一步会同时完成“移除容器里原有的旧格子”和“把文档片段中的全部新格子插入容器”两件事。如果要兼容非常老旧的浏览器环境,也可以先设 container.textContent = "" 再调用 container.appendChild(fragment)。

三、完整可运行示例与结果验证

把下面完整代码保存为本地文件 checkerboard.html,直接用任意现代浏览器打开即可运行。页面顶部还带有一个尺寸选择器,用来验证不同阶数(如 6×6、8×8、10×10)下的动态重绘效果。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>动态黑白棋盘格示例</title>
  <style>
    body {
      margin: 0;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 16px;
      background-color: #f5f4f0;
      font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
    }

    .controls {
      display: flex;
      align-items: center;
      gap: 10px;
      font-size: 14px;
    }

    .board {
      width: min(88vw, 480px);
      display: grid;
      grid-template-columns: repeat(var(--board-size, 8), 1fr);
      border: 2px solid #222222;
      box-sizing: border-box;
    }

    .cell {
      aspect-ratio: 1 / 1;
    }

    .cell-light {
      background-color: #ffffff;
    }

    .cell-dark {
      background-color: #222222;
    }
  </style>
</head>
<body>
  <div class="controls">
    <label for="size-select">棋盘尺寸:</label>
    <select id="size-select">
      <option value="6">6 × 6</option>
      <option value="8" selected>8 × 8</option>
      <option value="10">10 × 10</option>
    </select>
    <span id="status-text"></span>
  </div>

  <div id="chessboard" class="board"></div>

  <script>
    function renderCheckerboard(container, size = 8) {
      if (!container) {
        throw new Error("未找到棋盘挂载容器");
      }
      if (!Number.isInteger(size) || size <= 0) {
        throw new RangeError("棋盘尺寸必须是大于 0 的整数");
      }

      container.style.setProperty("--board-size", String(size));
      const fragment = document.createDocumentFragment();

      for (let row = 0; row < size; row += 1) {
        for (let col = 0; col < size; col += 1) {
          const cell = document.createElement("div");
          const isLight = (row + col) % 2 === 0;
          cell.className = isLight ? "cell cell-light" : "cell cell-dark";
          cell.dataset.row = String(row);
          cell.dataset.col = String(col);
          fragment.appendChild(cell);
        }
      }

      container.replaceChildren(fragment);
    }

    const boardEl = document.getElementById("chessboard");
    const sizeSelect = document.getElementById("size-select");
    const statusText = document.getElementById("status-text");

    function updateBoard() {
      const size = Number(sizeSelect.value);
      renderCheckerboard(boardEl, size);
      statusText.textContent = `当前格子数:${boardEl.children.length}`;
    }

    sizeSelect.addEventListener("change", updateBoard);
    updateBoard();
  </script>
</body>
</html>
预期运行表现(示例验证):
1. 首次打开 checkerboard.html 时,页面中央显示一个 8×8 正方形棋盘格,左上角第一格为白色,向右侧与向下方逐格黑白交替,状态文字显示“当前格子数:64”。
2. 打开浏览器开发者工具的“元素(Elements)”面板展开 <div id="chessboard">,可以看到内部按行顺序排列的 64 个子节点,第 1 个节点为 <div class="cell cell-light" data-row="0" data-col="0">,第 9 个节点(第 2 行第 1 列)自动翻转为 cell-dark。
3. 将下拉框切换为 10 × 10 后,旧的 64 个节点被替换,页面无闪烁地重绘为 100 个等比缩小的格子,状态文字更新为“当前格子数:100”。

四、两种常见 DOM 生成方式的对比与选择

在实现动态棋盘格时,除了上面用的 createElement + DocumentFragment,另一类常见写法是先拼接好 HTML 字符串,再通过 container.innerHTML = htmlString 一次性写入。在同样生成 N × N 个棋盘格、需要后续维护或交互的条件下,这两种写法的核心差异体现在处理链路上。

JavaScript通过DOM操作动态生成黑白棋盘格
图 3:createElement + DocumentFragment 与字符串拼接 + innerHTML 的处理链路对比
对比方面 createElement + DocumentFragment 字符串拼接 + innerHTML
节点引用与局部更新 生成时即持有每个格子对象,可存入二维数组方便后续直接改颜色或放棋子。 写入时只是一段字符串,渲染后若想修改特定格子,需要再次用选择器查找节点。
数据安全(含动态文本时) 若要在格子内显示坐标文字或用户输入标签,使用 textContent 不会把文本解析成脚本。 如果拼入未转义的外部文本,可能触发非预期标签解析或 XSS 风险。
已有子节点处理 使用 appendChild 时可保留旧节点追加新格;使用 replaceChildren 则显式替换。 每次赋值都会销毁容器内全部既有子节点及其单独绑定的事件监听器。

这些差异决定了两种方案各自适合的场景:如果只是在初始化时输出一张纯展示用的静态棋盘,innerHTML 写法简短直观;如果棋盘后面还要做成可落子、可高亮可走路径、或者格子内会显示动态文本的交互组件,采用 createElement + DocumentFragment 能直接保留节点引用并避免文本解析风险,后续维护成本更低。

五、常见问题与排查方法

实际编写时,即使逻辑大方向正确,也容易因为细节疏忽导致棋盘显示异常。下面三个问题最常见,排查时可以直接对照检查。

1. 棋盘变成一列白一列黑的竖条纹

现象:所有格子都生成了,但没有错位效果,呈现整列纯白、整列纯黑。
原因与修复:往往是用单层循环 for (let i = 0; i < size * size; i++) 并直接按 i % 2 决定颜色。当 size 为偶数(如标准 8×8)时,第 1 行结束于奇数索引,第 2 行又从偶数索引开始,每行第一个格子颜色完全相同。改为双层循环并使用 (row + col) % 2,或在单层循环中先算出 const row = Math.floor(i / size) 与 const col = i % size 再求和取余即可解决。

2. 节点已经生成,但页面上看不到棋盘

现象:在开发者工具里能看到 64 个 <div class="cell ...">,但页面高度为 0,只有一条细边框。
原因与修复:动态创建的 <div> 内部没有文字内容,默认高度由内容撑开,没有内容时高度为 0。确保 CSS 中给 .cell 设置了 aspect-ratio: 1 / 1(配合容器固定宽度或 min(88vw, 480px)),或者直接给 .cell 指定明确的 width 和 height。

3. 切换尺寸或重复调用后格子数量翻倍

现象:第一次生成 8×8 正常,调用函数切换为 6×6 后,棋盘下方多出一截多余格子。
原因与修复:重新渲染时使用了 container.appendChild(fragment) 而忘记先清空容器,导致新旧节点叠加。用 container.replaceChildren(fragment) 替代 appendChild,或在挂载前执行 container.textContent = "",就能保证每次渲染后的子节点总数严格等于 size * size。

JavaScript通过DOM操作动态生成黑白棋盘格
图 4:JavaScript 动态生成黑白棋盘格的核心要素与常见问题排查总结

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章