技术求助:基于CSS Grid与Vanilla JS的时间追踪仪表盘无法通过.innerHTML()正确渲染JSON数据
问题分析与解决方案
先解决最核心的数据无法正确渲染到卡片的问题,再重构代码来符合DRY原则,一步步来:
1. 数据渲染失败的核心原因
看你代码里的几个关键问题:
- 变量名错误:在
daily和weekly分支里,你把getData()返回的数组命名为stats,但遍历里却用了stat.timeframes...——这个stat根本没定义!所以插值都是无效的,自然渲染不出来。 - 未做数据与卡片的一一映射:你的
activities是多个卡片元素,stats是多个统计条目,需要把每个stat对应到对应的activity上,而不是给所有卡片都塞同一个内容(这也是之前重复显示的原因)。 - NodeList操作错误:在
monthly分支里,你直接给activities(NodeList集合)设置innerHTML,这是无效的,必须遍历每个元素单独设置。
2. 修复渲染逻辑的关键调整
先把数据和卡片的对应关系理清楚:拿到stats数组后,要同时遍历stats和activities,通过索引一一匹配(假设它们的顺序是对应的,比如第一个统计条目对应第一个卡片)。
3. 重构代码实现DRY原则
三个时间范围的逻辑几乎完全一致,只是时间范围字符串不同,我们可以把重复逻辑提取成通用函数,避免冗余代码。
完整优化后的代码
// 提取通用渲染函数,接收时间范围参数 function renderActivities(timeframe) { btnStyles(timeframe); getData().then(stats => { // 将NodeList转为数组,方便遍历操作 const activities = Array.from(document.querySelectorAll(".previous-activity")); // 一一对应每个统计条目和卡片 stats.forEach((stat, index) => { if (activities[index]) { // 确保存在对应的卡片元素 const timeData = stat.timeframes[timeframe]; activities[index].innerHTML = ` <p class='curr-hrs'> ${timeData.current} hrs</p> <p class='previous-hrs'>${getPreviousLabel(timeframe)} - ${timeData.previous} hrs</p> `; } }); }); } // 提取不同时间范围的前置描述文本 function getPreviousLabel(timeframe) { switch(timeframe) { case "daily": return "Last Day"; case "weekly": return "Last Week"; case "monthly": return "Last Month"; default: return "Last Period"; } } // 简化事件监听逻辑 statBtn.addEventListener("click", (e) => { const id = e.target.id; if (["daily", "weekly", "monthly"].includes(id)) { renderActivities(id); } });
关键说明
- 变量名修正:现在用
stat指代stats数组里的每个条目,通过索引和activities数组元素一一绑定,确保每个卡片渲染对应的数据。 - 通用函数复用:
renderActivities统一处理数据请求和渲染逻辑,三个按钮分支只需要传入对应的时间范围参数即可,彻底避免重复代码。 - 文本逻辑分离:用
getPreviousLabel函数管理不同时间范围的描述文本,让代码结构更清晰。 - 数组转换:把
querySelectorAll返回的NodeList转成数组,避免NodeList的遍历限制,操作更灵活。
这样修改后,数据应该能正确渲染到对应卡片里,同时代码也符合DRY原则啦。
内容的提问来源于stack exchange,提问作者Evan Parker
相关产品推荐
相关产品推荐

