You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多个自定义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>

问题原因

  1. 使用var声明selectBtn、selectedValue、optionsList,这些变量属于函数级作用域,每次循环都会被重新赋值,所有事件回调共享最后一次循环的变量引用,导致操作都指向最后一个元素。
  2. 内层循环用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 10:48:08