JS中出现新子元素包含父元素的DOM异常问题求助
问题解决:贷款模拟页面的DOM错误与多笔贷款显示问题
错误原因分析
1. DOM异常错误
在classContenedor函数中执行了contenedor.append(contenedor),试图将容器元素添加到自身内部,违反DOM树层级规则,直接触发Uncaught DOMException错误。这行代码无实际作用,需直接删除。
2. 多笔贷款无法显示的问题
- 重复绑定提交事件:每次循环都给同一个
formPrestamo添加submit监听,导致提交时触发多次回调,数据逻辑混乱。 - 结果内容被覆盖:使用
contenedor.innerHTML = ...会清空原有内容,新贷款结果会替换旧记录,无法保留多笔数据。 - 表单状态未重置:提交第一笔贷款后,未重置表单也未跟踪当前填写进度,导致第二笔数据覆盖第一笔的
Prestamo实例。
修改后的代码
HTML代码(修正闭合标签+添加输入限制)
<div class="container principal pt-2 mb-3"> <div class='row col-sm-12 mb-1'> <form id="formulario"> <div class="row col-sm-12 mb-1 d-flex justify-content-evenly"> <label class="form-label col-12 col-lg-6 titulos" for='cantidadPrestamos'>¿Cuantos prestamos queres solicitar?</label> <input class="col-12 col-lg-4 main m-2" id='cantidadPrestamos' name='full-name' type='number' min="1" max="2"/> <input class="col-12 col-lg-auto main m-2" type="submit" value="Enviar"> </div> </form> </div> </div> <div id="display" class="container principal mb-2 d-none"> <div class='row col-sm-12 mb-1'> <form id="formPrestamo"> <div class="titulos mb-3 p-1 pt-3"> Necesitamos estos datos para cotizar tu prestamo: </div> <div class="row col-sm-12 mb-1"> <label class="form-label col-12 col-lg-4" for='capital'>Capital (Minimo $10.000)</label> <input class="col-12 col-lg-8 main mb-2" id='capital' name='full-name' type='number' min="10000"/> </div> <div class='row col-sm-12 mb-1'> <label class="form-label col-12 col-lg-4" for='cuotas'>Cuotas (Maximo 60)</label> <input class="col-12 col-lg-8 main mb-2" id='cuotas' name='cuotas' type='number' max="60" min="1"/> </div> <div class='row col-sm-12 mb-1'> <label class="form-label col-12 col-lg-4" for='interes'>Interes</label> <input class="col-12 col-lg-8 mb-2 main" id='interes' name='interes' type="number" min="0"></input> </div> <div class='row mb-3 d-flex justify-content-center'> <input class="col-12 col-lg-6 main m-2" type="submit" value="Enviar"> </div> </form> </div> </div> <div id="contenedor"></div>
JavaScript代码
let cantidadPrestamos = 0; let prestamos = []; let currentLoanIndex = 0; const formulario = document.getElementById("formulario"); const formPrestamo = document.getElementById("formPrestamo"); const contenedor = document.getElementById("contenedor"); const display = document.getElementById("display"); // 全局绑定表单事件(仅绑定一次) formulario.addEventListener("submit", validateForm); formPrestamo.addEventListener("submit", validateFormPrestamo); function validateForm(event) { event.preventDefault(); cantidadPrestamos = parseInt(event.target[0].value); // 重置申请状态 prestamos = []; currentLoanIndex = 0; contenedor.innerHTML = ""; // 显示贷款表单并重置 display.classList.remove("d-none"); formPrestamo.reset(); } function Prestamo(capital, cantidadCuotas, interes, cuotaMes, devolucionTotal) { this.capital = capital; this.cantidadCuotas = cantidadCuotas; this.interes = interes; this.cuotaMes = cuotaMes; this.devolucionTotal = devolucionTotal; } function validateFormPrestamo(event) { event.preventDefault(); const formData = event.target; const capital = parseInt(formData[0].value); const cuotas = parseInt(formData[1].value); const interes = parseFloat(formData[2].value) / 100; // 输入验证 if (capital < 10000) { showError("El monto solicitado es insuficiente. El minimo es $10.000"); return; } if (cuotas > 60) { showError("Excediste la cantidad de cuotas habilitadas"); return; } // 计算贷款数据 const tasa = (interes) => (1 + interes) ** (1/12) - 1; const cuotaMensual = (capital, tasa, cuotas) => (tasa * capital) / (1 - (1 + tasa) ** -cuotas); const monthlyRate = tasa(interes); const monthlyPayment = cuotaMensual(capital, monthlyRate, cuotas); const totalRepayment = monthlyPayment * cuotas; // 保存贷款实例 const prestamo = new Prestamo(capital, cuotas, interes, monthlyPayment, totalRepayment); prestamos.push(prestamo); // 更新显示所有贷款 renderLoans(); // 控制表单显示逻辑 currentLoanIndex++; if (currentLoanIndex >= cantidadPrestamos) { display.classList.add("d-none"); } else { formPrestamo.reset(); } } function renderLoans() { contenedor.className = "container principal subtitulos"; contenedor.innerHTML = ""; // 渲染每一笔贷款 prestamos.forEach((prestamo, index) => { const loanElement = document.createElement("div"); loanElement.className = "loan-item mb-4 p-3 border"; loanElement.innerHTML = ` <h3>Prestamo ${index + 1}: $${prestamo.capital}</h3> <p>Interes: ${prestamo.interes * 100}%</p> <p>Cuotas: ${prestamo.cantidadCuotas}</p> <p>Cuota mensual: $${prestamo.cuotaMes.toFixed(2)}</p> <p>Total a devolver: $${prestamo.devolucionTotal.toFixed(2)}</p> `; contenedor.appendChild(loanElement); }); } function showError(message) { contenedor.className = "container principal subtitulos"; contenedor.innerHTML = `<div class="alert alert-danger">${message}</div>`; }
关键修改点
- 移除错误的DOM操作:删除
contenedor.append(contenedor),将样式设置整合到渲染函数中。 - 单次事件绑定:将
formPrestamo的提交事件移到全局绑定,避免重复绑定导致的逻辑冲突。 - 跟踪申请进度:用
currentLoanIndex记录已完成的贷款数量,判断是否需要继续显示表单。 - 保留多笔记录:通过创建独立DOM元素并追加到容器,或清空后重新渲染所有贷款,避免内容覆盖。
- 状态重置:每次发起新申请时,重置贷款数组、索引和容器内容,清除旧数据干扰。
- 输入优化:HTML添加
min/max限制输入范围,JS中补充验证并通过独立函数显示错误信息。
内容的提问来源于stack exchange,提问作者Nicolás Toranzo
相关产品推荐
相关产品推荐

