使用forEach遍历数组时始终获取最后一个元素值的问题解决
问题:点击批量生成的选项卡始终获取最后一个对象的价格
我正在开发选项卡(tabs)功能,通过forEach方法在DOM中批量生成选项卡块,每个块对应数组中包含价格的对象。但点击选项卡时,始终只能获取到数组最后一个对象的价格值,请问如何实现点击对应块时获取其正确的价格?
原始代码
JavaScript
let plansVal = [{ id: 1, price: "2,90", follows: "50" }, { id: 2, price: "3,90", follows: "150" }, { id: 3, price: "5,90", follows: "250" }, { id: 4, price: "150,90", follows: "350" }, { id: 5, preco: "300,90", follows: "450" }, { id: 6, preco: "9,90", follows: "1000" }, { id: 7, price: "19,90", follows: "1500" }, { id: 8, price: ",30", follows: "2000" }, { id: 9, price: "59,90", follows: "3000" }, { id: 10, price: "99,90", follows: "3500" }, ]; const tabs = add => { const plansContent = $(".plans-content"); plansContent.append(` <div id="plan${add.id}" class="plan bg-dgrade-r-s"> <div class="plan-text"> <span><strong>${add.follows}</strong></span> <p>Seguidores</p> </div> </div> `); plansContent.click(function() { console.log(add.price); }); } plansVal.forEach(add => tabs(add));
CSS
.plans { max-width: max-content; padding: 0; } .plans .plans-title { text-align: center; font-size: 1.3rem; margin-bottom: 25px; } .plans .plans-title b { color: var(--roxo); } .vector_donthavelikes { width: 90%; } .vector_donthavelikes img { max-width: 100%; display: block; margin: auto; image-rendering: optimizeSpeed; } .plans .plans-content { display: flex; justify-content: center; flex-flow: row wrap; place-items: center; } .plans .plans-content .plan { min-width: 125px; max-width: 125px; margin: 5px 3px; cursor: pointer; } .plans .plans-content .plan .plan-text { background-color: #fff; height: 80px; padding: 10px 5px; line-height: 20px; } .plans .plans-content .plan.active .plan-text { background-color: transparent; } .plans .plans-content .plan.active .plan-text p { color: #fff; } .plans .plans-content .plan .plan-text span { font-size: 1.2rem; margin-top: 10px; display: block; } .plans .plans-content .plan .plan-text p { font-size: 0.9rem; color: var(--cinza-escuro); } .plans .plans-content .plan.active { color: #fff; } .plans .plans-content .plan, .plans .plans-content .plan.active { padding: 7px; border-radius: 5px; text-align: center; } .plans .plans-content .plan.active { background-image: -moz-linear-gradient( -151deg, rgb(143, 23, 219) 0%, rgb(220, 134, 125) 100%); background-image: -webkit-linear-gradient( -151deg, rgb(143, 23, 219) 0%, rgb(220, 134, 125) 100%); background-image: -ms-linear-gradient( -151deg, rgb(143, 23, 219) 0%, rgb(220, 134, 125) 100%); } .plans div .plan-value { font-size: 1.7rem; font-weight: 550; } .plans div .plan-value sup { color: var(--cinza); }
HTML
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <div class="plans mx-auto col-sm-5"> <div class="d-flex align-items-center p-3"> <h3 class="plans-title"> É hora de dar engajamento ao seu Instagram com Seguidores Reais. <b>Entrega em poucos minutos.</b> </h3> <div class="vector_donthavelikes"> <img src="/src/imgs/others/donthavelikes.png" alt=""> </div> </div> <div class="plans-content"></div> <div class="d-flex mt-4 flex-column col-sm-4 col-md-5 mx-auto text-center"> <p id="planValue" class="plan-value"><sup>R$</sup> 3.99</p> <a href="#" class="btn-comn mx-auto"> Buy </a> </div> </div>
问题原因
- 重复绑定事件到父元素:每次循环都给
plansContent绑定一次click事件,最终父元素上叠加了10个点击事件,且由于闭包特性,所有事件中的add变量最终都会指向数组的最后一个元素。 - 未区分点击目标:事件绑定在父元素上,但没有判断点击的是哪个子选项卡,无法关联到对应的对象数据。
解决方案
方案1:给每个选项卡单独绑定事件
生成每个选项卡后,直接给该元素绑定点击事件,确保每个事件捕获当前循环的add对象:
const tabs = add => { const plansContent = $(".plans-content"); // 创建单个选项卡jQuery对象 const $plan = $(` <div id="plan${add.id}" class="plan bg-dgrade-r-s"> <div class="plan-text"> <span><strong>${add.follows}</strong></span> <p>Seguidores</p> </div> </div> `); // 绑定点击事件,兼容price/preco字段 $plan.click(function() { const price = add.price || add.preco; console.log(price); // 更新页面价格显示 $("#planValue").html(`<sup>R$</sup> ${price}`); }); plansContent.append($plan); } plansVal.forEach(add => tabs(add));
方案2:事件委托+自定义data属性(推荐)
只给父元素绑定一次点击事件,通过data-*属性存储价格,点击时从目标元素获取数据,性能更优:
// 先统一处理数组中的价格字段,避免后续判断 plansVal.forEach(item => { item.price = item.price || item.preco; }); const tabs = add => { const plansContent = $(".plans-content"); plansContent.append(` <div id="plan${add.id}" class="plan bg-dgrade-r-s" data-price="${add.price}"> <div class="plan-text"> <span><strong>${add.follows}</strong></span> <p>Seguidores</p> </div> </div> `); } plansVal.forEach(add => tabs(add)); // 父元素绑定一次事件,通过事件委托处理子元素点击 $(".plans-content").click(function(e) { // 找到点击的.plan元素 const $targetPlan = $(e.target).closest(".plan"); if ($targetPlan.length) { const price = $targetPlan.data("price"); console.log(price); $("#planValue").html(`<sup>R$</sup> ${price}`); } });
方案3:闭包捕获循环变量(不推荐)
通过立即执行函数固定每次循环的add变量,但会导致父元素绑定多个事件,性能较差:
const tabs = add => { const plansContent = $(".plans-content"); plansContent.append(` <div id="plan${add.id}" class="plan bg-dgrade-r-s"> <div class="plan-text"> <span><strong>${add.follows}</strong></span> <p>Seguidores</p> </div> </div> `); // 用立即执行函数捕获当前add (function(currentAdd) { plansContent.click(function(e) { const $target = $(e.target).closest(`#plan${currentAdd.id}`); if ($target.length) { const price = currentAdd.price || currentAdd.preco; console.log(price); $("#planValue").html(`<sup>R$</sup> ${price}`); } }); })(add); } plansVal.forEach(add => tabs(add));
说明:方案2是最优选择,既避免重复绑定事件,又能清晰地关联元素与数据,同时解决了数组中价格字段不一致的问题。
内容的提问来源于stack exchange,提问作者Carlos Eduardo
相关产品推荐
相关产品推荐

