本文讲解如何避免因直接修改 dom 导致初始值丢失的问题,推荐将原始数据作为“单一数据源”(source of truth)存储在 javascript 对象中,并基于该源进行动态计算与渲染。
本文讲解如何避免因直接修改 dom 导致初始值丢失的问题,推荐将原始数据作为“单一数据源”(source of truth)存储在 javascript 对象中,并基于该源进行动态计算与渲染。
在前端开发中,一个常见误区是:将初始数值硬编码在 HTML 中(如 <span class="span">200</span>),然后在 JS 中反复读取 .innerText 并覆盖它——这会导致原始值永久丢失,后续无法还原或按比例重新计算。你当前的 calcAmount() 函数正是如此:每次执行都会改写 DOM 中的文本内容,下次调用时 parseInt(portions[i].innerText) 读到的是已缩放后的值,而非原始配方量,从而引发计算错误。
正确做法是:分离数据与视图。将所有原始配料量统一存入一个 JS 数据结构(如数组或对象),DOM 仅作为渲染输出;所有计算均基于该数据源,而非反向解析 DOM。
以下是一个优化后的完整实现示例:
// ✅ 单一数据源:原始配方(不可变基准)const RECIPE = [ { name: 'Mehl', amount: 200, unit: 'g' }, { name: 'Zucker', amount: 200, unit: 'g' }, { name: 'Kakao', amount: 50, unit: 'g' }];// ✅ 渲染函数:根据当前份量更新 DOMfunction renderIngredients(size) { const spans = document.querySelectorAll('.span'); spans.forEach((span, i) => { if (i < RECIPE.length) { const scaled = Math.round(RECIPE[i].amount * size); span.textContent = scaled; // 可选:同步更新对应单位文本(如 "g Mehl" → 需确保结构可定位) const parentP = span.closest('p'); if (parentP) { const textNodes = Array.from(parentP.childNodes) .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent.trim()); if (textNodes.length > 0) { // 简单策略:保留单位和名称,仅更新数字部分(更健壮方案建议用 data-* 属性) const restText = textNodes[0].textContent.replace(/^d+s*/, ''); parentP.innerHTML = `<span class="span">${scaled}</span>${restText}`; } } } });}// ✅ 主计算逻辑:始终基于 RECIPE 源数据function calcAmount() { const input = document.getElementById('portion'); const size = parseInt(input.value, 10); if (isNaN(size) || size < 1 || size > 5) { alert('Bitte geben Sie eine ganze Zahl zwischen 1 und 5 ein.'); return; } renderIngredients(size); input.value = ''; // 重置输入框}// ✅ 页面加载后初始化显示原始份量(1份)document.addEventListener('DOMContentLoaded', () => { renderIngredients(1);});
? 关键改进点说明:
注意事项:
通过将状态管理从 DOM 提升至 JavaScript 层,你的代码将更可靠、更易维护,也真正具备响应式计算能力。