多个自定义Select元素行为异常问题排查
自定义Select循环绑定事件异常问题
问题现象
页面中创建多个自定义Select元素后,遍历绑定事件监听器时,所有事件触发后都作用在最后一个元素上。
原代码
JavaScript
let customSelect = document.querySelectorAll(".custom-select"); for (let i = 0; i < 2; i++) { var selectBtn = customSelect[i].querySelector(".select-button"); var selectedValue = customSelect[i].querySelector(".selected-value"); var optionsList = customSelect[i].querySelectorAll(".select-dropdown li"); // Toggle dropdown visibility when button is clicked selectBtn.addEventListener("click", function(event) { customSelect[i].classList.toggle("active"); }); // Handle option selection for (var x = 0; x < optionsList.length; x++) { optionsList[x].addEventListener("click", function(event) { selectedValue.textContent = this.textContent; customSelect[i].classList.remove("active"); }); } }
CSS
.select-button { width: 100%; font-size: 1.15rem; background-color: #fff; padding: 0.675em 1em; border: 1px solid #caced1; border-radius: 0.25rem; cursor: pointer; display: flex; justify-content: space-between; align-items: center; } .selected-value { text-align: left; } .arrow { border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 6px solid #000; transition: transform ease-in-out 0.3s; } .select-dropdown { position: absolute; list-style: none; width: 100%; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); background-color: #fff; border: 1px solid #caced1; border-radius: 4px; padding: 10px; margin-top: 10px; max-height: 200px; overflow-y: auto; transition: 0.5s ease; transform: scaleY(0); opacity: 0; visibility: hidden; } .select-dropdown:focus-within { box-shadow: 0 10px 25px rgba(94, 108, 233, 0.6); } .select-dropdown li { position: relative; cursor: pointer; display: flex; gap: 1rem; align-items: center; } .select-dropdown li label { width: 100%; padding: 8px 10px; cursor: pointer; } .select-dropdown::-webkit-scrollbar { width: 7px; } .select-dropdown::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 25px; } .select-dropdown::-webkit-scrollbar-thumb { background: #ccc; border-radius: 25px; } .select-dropdown li:hover, .select-dropdown input:checked~label { background-color: #f2f2f2; } .select-dropdown input:focus~label { background-color: #dfdfdf; } .select-dropdown input[type="radio"] { position: absolute; left: 0; opacity: 0; } .custom-select.active .arrow { transform: rotate(180deg); } .custom-select.active .select-dropdown { opacity: 1; visibility: visible; transform: scaleY(1); }
HTML
<div id="newmatpe" class="custom-select"> <button class="select-button"> <span class="selected-value" id="a1"> habadaba</span> <span class="arrow"></span> </button> <ul class="select-dropdown"> <li> <input type="radio" id="radioa1" name="newmatpe" /> <label for="radioa1">shubi</label> </li> <li> <input type="radio" id="radioa2" name="newmatpe" /> <label for="radioa2">dubi</label> </li> <li> <input type="radio" id="radioa3" name="newmatpe" /> <label for="radioa3">Bubi</label> </li> <li> <input type="radio" id="radioa4" name="newmatpe" /> <label for="radioa4">dubi</label> </li> <li> <input type="radio" id="radioa5" name="newmatpe" /> <label for="radioa5">FM2</label> </li> <li> <input type="radio" id="radioa6" name="newmatpe" /> <label for="radioa6">AM1</label> </li> <li> <input type="radio" id="radioa7" name="newmatpe" /> <label for="radioa7">Really</label> </li> <li> <input type="radio" id="radioa8" name="newmatpe" /> <label for="radioa8">cool</label> </li> <li> <input type="radio" id="radioa9" name="newmatpe" /> <label for="radioa9">other</label> </li> </ul> </div> </br> </br> <div id="existingmatpe" class="custom-select"> <button class="select-button"> <span class="selected-value" id="a2">shlala</span> <span class="arrow"></span> </button> <ul class="select-dropdown"> <li> <input type="radio" id="radiob1" name="existingmatpe" /> <label for="radiob1">balala</label> </li> <li> <input type="radio" id="radiob2" name="existingmatpe" /> <label for="radiob2">malala</label> </li> <li> <input type="radio" id="radiob3" name="existingmatpe" /> <label for="radiob3">talala</label> </li> <li> <input type="radio" id="radiob4" name="existingmatpe" /> <label for="radiob4">googoo</label> </li> <li> <input type="radio" id="radiob5" name="existingmatpe" /> <label for="radiob5">gaga</label> </li> <li> <input type="radio" id="radiob6" name="existingmatpe" /> <label for="radiob6">yalala</label> </li> <li> <input type="radio" id="radiob7" name="existingmatpe" /> <label for="radiob7">malala</label> </li> <li> <input type="radio" id="radiob8" name="existingmatpe" /> <label for="radiob8">sdsdsds</label> </li> <li> <input type="radio" id="radiob9" name="existingmatpe" /> <label for="radiob9">other</label> </li> </ul> </div>
问题原因
- 使用
var声明selectBtn、selectedValue、optionsList,这些变量属于函数级作用域,每次循环都会被重新赋值,所有事件回调共享最后一次循环的变量引用,导致操作都指向最后一个元素。 - 内层循环用
var x,同样存在作用域提升问题,加剧了变量共享的冲突。
修复方案
方案1:替换var为let
利用let的块级作用域特性,让每个循环迭代的变量独立存在,避免回调引用共享变量:
let customSelect = document.querySelectorAll(".custom-select"); // 循环条件改为根据实际元素数量遍历,更灵活 for (let i = 0; i < customSelect.length; i++) { // 用let声明,每个迭代拥有独立的变量 let selectBtn = customSelect[i].querySelector(".select-button"); let selectedValue = customSelect[i].querySelector(".selected-value"); let optionsList = customSelect[i].querySelectorAll(".select-dropdown li"); selectBtn.addEventListener("click", function(event) { customSelect[i].classList.toggle("active"); }); for (let x = 0; x < optionsList.length; x++) { optionsList[x].addEventListener("click", function(event) { selectedValue.textContent = this.textContent; customSelect[i].classList.remove("active"); }); } }
方案2:使用forEach遍历(更优雅)
直接遍历每个自定义Select元素,避免依赖循环索引,从根源上规避作用域问题:
document.querySelectorAll(".custom-select").forEach(select => { const selectBtn = select.querySelector(".select-button"); const selectedValue = select.querySelector(".selected-value"); const optionsList = select.querySelectorAll(".select-dropdown li"); selectBtn.addEventListener("click", () => { select.classList.toggle("active"); }); optionsList.forEach(option => { option.addEventListener("click", () => { selectedValue.textContent = option.textContent; select.classList.remove("active"); }); }); });
内容的提问来源于stack exchange,提问作者user23654508
相关产品推荐
相关产品推荐

