基于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
相关产品推荐
相关产品推荐

