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

基于DOM实现点击器游戏:多函数调用与元素追加问题求助

问题描述

我正在练习DOM开发点击器游戏,核心逻辑是点击工具增加计数,工具数量足够时可解锁建造者。现在卡在renderBuilders函数的实现上,需要满足以下要求:

  • 循环处理给定的建造者数据,调用已有函数创建并追加div到建造者容器
  • 自动解锁工具数量足够的建造者
  • 只渲染已解锁的建造者
  • 每次渲染前清空容器内的旧元素

同时还遇到一个错误:TypeError: Attempted to wrap getElementById which is already wrapped,出现在updateToolsPerSecondView函数调用时。

相关数据

const data = {
  tools: 200,
  builders: [
    { id: 'builder_A', price: 100, unlocked: false },
    { id: 'builder_B', price: 400, unlocked: false },
    { id: 'builder_C', price: 700, unlocked: false }
  ]
};

测试规范

  • 调用document.getElementById()获取容器,追加元素后容器子元素数量>0
  • 调用renderBuilders(data)后,data.builders[0]和data.builders[1]的unlocked为true,data.builders[2]为false
  • 仅渲染已解锁建造者,容器子元素数量为2,第一个子元素有5个子节点
  • 手动添加假元素后调用renderBuilders(data),容器子元素数量仍为2,第一个子元素结构符合要求

现有问题代码

function renderBuilders(data) {
  let builderContainer = document.getElementById('builder_container');
  for (let i = 0; i < data.length; i++) {
    const newBuilder = currentBuilder; // 不确定此方式是否正确
    currentBuilder = makeBuilderDiv(); // 该函数用于创建建造者div
     
    let deletedBuilder = deleteAllChildNodes(); // 用于清空容器子元素的函数
    let unlockedBuilder = getUnlockedBuilders(); // 用于解锁符合条件建造者的函数
  }
  builderContainer.appendChild(builder);
}
function updateToolsPerSecondView(tps) {
  const tpsView = document.getElementById('tps');
  tpsView.innerText = tps;
}
// 错误信息:TypeError: Attempted to wrap getElementById which is already wrapped

解决方案

1. 修复renderBuilders函数逻辑

原代码存在循环对象错误、变量未定义、逻辑顺序颠倒等问题,修正后的代码如下:

function renderBuilders(data) {
  const builderContainer = document.getElementById('builder_container');
  
  // 1. 先清空容器(满足测试规范4)
  deleteAllChildNodes(builderContainer); // 给清空函数传入目标容器

  // 2. 处理建造者解锁逻辑(满足测试规范2)
  function getUnlockedBuilders() {
    data.builders.forEach(builder => {
      // 工具数量达标则解锁建造者
      if (data.tools >= builder.price) {
        builder.unlocked = true;
      }
    });
    // 过滤返回已解锁的建造者列表
    return data.builders.filter(builder => builder.unlocked);
  }

  const unlockedBuilders = getUnlockedBuilders();

  // 3. 循环已解锁建造者,创建并追加元素(满足测试规范1、3)
  unlockedBuilders.forEach(builder => {
    // 传入建造者数据生成对应结构的div
    const builderDiv = makeBuilderDiv(builder);
    builderContainer.appendChild(builderDiv);
  });
}

// 补充实现清空容器的函数(若未正确实现)
function deleteAllChildNodes(container) {
  while (container.firstChild) {
    container.removeChild(container.firstChild);
  }
}

关键修正点:

  • 循环对象改为已解锁的建造者列表,避免错误遍历整个data对象
  • 清空容器操作放在循环前,避免重复清空
  • 先执行解锁逻辑,再过滤已解锁项进行渲染
  • 给清空函数传入目标容器,确保操作对象正确
  • 调用makeBuilderDiv时传入建造者数据,保证生成的div结构符合测试要求

2. 解决TypeError: Attempted to wrap getElementById which is already wrapped错误

这个错误几乎都是**测试框架(如Jest)中重复mock了document.getElementById**导致的。比如多个测试用例重复调用jest.spyOn(document, 'getElementById'),但未在测试间隙清除mock。

解决方法:

  • 在测试用例的afterEach或beforeEach中添加mock清除逻辑:
afterEach(() => {
  jest.clearAllMocks();
  // 或针对getElementById单独恢复:
  // jest.restoreAllMocks();
});
  • 若手动mock,确保每次mock前检查状态,避免重复包装目标方法

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 13:27:19