JavaScript带表单增删的自动求和实现及UIKit模态框求和失效排查
解决动态表单的自动求和失效问题
我明白你遇到的麻烦了——你用UIKit模态框动态添加/移除表单时,只有第一个表单能触发自动求和,新增的完全没反应。这其实是个很常见的事件绑定问题,我来给你拆解原因和解决方案:
问题根源
你最初写的onFocus="startCalc();"和onBlur="stopCalc();"是直接绑定在页面加载时就存在的元素上的。但动态新增的表单元素是在页面加载后才创建的,这些元素并没有被绑定上这些事件,自然不会触发求和计算。另外,你的求和逻辑可能没有区分不同的表单,导致就算事件绑定了,也可能会计算错误的数值。
推荐解决方案:事件委托(高效适配动态元素)
事件委托利用了DOM的事件冒泡机制,把事件绑定到父容器上,而不是每个单独的输入框。这样不管是初始的还是动态新增的元素,只要触发事件,都会被父容器捕获到。
第一步:调整HTML结构
给所有表单加上统一的类名,方便我们定位,同时把所有表单放在一个父容器里:
<div id="formContainer"> <!-- 初始表单 --> <form class="autoSumForm"> <b>aa</b> <input type='text' name='aa' class="sum-input" style="text-align:right;" size='23' /> <b>bb</b> <input type='text' name='bb' class="sum-input" style="text-align:right;" size='23' /> <b>总和</b> <input type='text' name='total' readonly style="text-align:right;" size='23' /> <button type="button" class="remove-form">移除表单</button> </form> </div> <button type="button" id="add-form">添加新表单</button>
第二步:重写JavaScript逻辑
使用事件委托绑定焦点事件,同时确保求和只针对当前输入框所在的表单:
// 用于存储计算定时器,避免多个表单互相干扰 let calcTimer; // 事件委托:监听所有求和输入框的focus事件 document.getElementById('formContainer').addEventListener('focus', function(e) { if (e.target.classList.contains('sum-input')) { startCalc(e.target); } }, true); // 事件委托:监听所有求和输入框的blur事件 document.getElementById('formContainer').addEventListener('blur', function(e) { if (e.target.classList.contains('sum-input')) { stopCalc(); } }, true); // 启动实时计算 function startCalc(inputElement) { // 获取当前输入框所在的表单 const currentForm = inputElement.closest('.autoSumForm'); // 每隔100ms更新一次总和,可根据需求调整间隔 calcTimer = setInterval(() => calculateSum(currentForm), 100); } // 停止实时计算 function stopCalc() { clearInterval(calcTimer); } // 计算单个表单的总和 function calculateSum(form) { // 获取当前表单内的aa、bb输入框和总和输入框 const aaValue = parseFloat(form.querySelector('input[name="aa"]').value) || 0; const bbValue = parseFloat(form.querySelector('input[name="bb"]').value) || 0; const totalInput = form.querySelector('input[name="total"]'); // 计算并赋值,保留两位小数(按需调整) totalInput.value = (aaValue + bbValue).toFixed(2); } // 添加新表单的逻辑 document.getElementById('add-form').addEventListener('click', function() { // 克隆初始表单(或者直接拼接HTML字符串,按需选择) const newForm = document.querySelector('.autoSumForm').cloneNode(true); // 清空新表单的所有输入值 newForm.querySelectorAll('input').forEach(input => input.value = ''); // 把新表单添加到容器里 document.getElementById('formContainer').appendChild(newForm); }); // 移除表单的事件委托 document.getElementById('formContainer').addEventListener('click', function(e) { if (e.target.classList.contains('remove-form')) { const formToRemove = e.target.closest('.autoSumForm'); // 至少保留一个表单,可根据需求修改 if (document.querySelectorAll('.autoSumForm').length > 1) { formToRemove.remove(); } } });
关键要点说明
- 事件委托:把事件绑定到父容器
formContainer上,所有动态新增的输入框触发focus/blur时,事件会冒泡到父容器,从而触发我们的逻辑。 - 表单隔离:用
closest('.autoSumForm')精准定位当前输入框所在的表单,确保每个表单的求和都是独立的,不会互相干扰。 - 定时器管理:每次启动计算前清空之前的定时器,避免多个定时器同时运行导致计算混乱。
备选方案:动态绑定事件(适合简单场景)
如果你不想用事件委托,也可以在创建新表单时,手动给输入框绑定onFocus和onBlur事件:
document.getElementById('add-form').addEventListener('click', function() { const newFormHtml = ` <form class="autoSumForm"> <b>aa</b> <input type='text' name='aa' style="text-align:right;" size='23' onFocus="startCalc(this);" onBlur="stopCalc();" /> <b>bb</b> <input type='text' name='bb' style="text-align:right;" size='23' onFocus="startCalc(this);" onBlur="stopCalc();" /> <b>总和</b> <input type='text' name='total' readonly style="text-align:right;" size='23' /> <button type="button" class="remove-form">移除表单</button> </form> `; document.getElementById('formContainer').insertAdjacentHTML('beforeend', newFormHtml); });
这种方式简单直接,但每次新增元素都要绑定事件,当表单数量较多时,性能不如事件委托。
内容的提问来源于stack exchange,提问作者Dzy
相关产品推荐
相关产品推荐

