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

纯JavaScript多级菜单问题:仅捕获首个元素,querySelectorAll报错

问题原因与解决方案

你遇到的报错很好理解:querySelectorAll()返回的是一个NodeList(类数组的元素集合),而addEventListener是单个DOM元素才有的方法,直接在集合上调用当然会报错啦~

原来用querySelector('.more')只生效第一个元素,是因为这个方法本来就只会返回匹配选择器的第一个元素,所以后面的.more元素都没绑定到事件。

下面给你两种可行的解决方案:

方案1:遍历元素集合,逐个绑定事件

通过forEach遍历querySelectorAll返回的NodeList,给每个.more元素单独绑定点击事件:

(function() { 
  // 获取所有带.more类的元素
  const moreItems = document.querySelectorAll('.more');
  
  // 遍历每个元素,绑定切换类名的事件
  moreItems.forEach(item => {
    item.addEventListener('click', function() { 
      this.classList.toggle('open') 
    }, false); 
  });
})();

注:如果需要兼容非常老旧的浏览器(比如IE11及以下),NodeList的forEach可能不支持,这时可以用普通的for循环替代:

for (let i = 0; i < moreItems.length; i++) {
  moreItems[i].addEventListener('click', function() {
    this.classList.toggle('open');
  }, false);
}

方案2:使用事件委托(更推荐)

如果页面后续可能动态添加.more元素,或者元素数量较多,事件委托会是更高效的选择——把事件绑定到父级容器(甚至document),通过事件冒泡来判断点击的目标元素:

(function() { 
  // 把事件绑定到document,也可以绑定到更接近的父级容器(比如所有ul的共同父元素)
  document.addEventListener('click', function(e) {
    // 检查点击的元素是否是.more,或者是否在.more元素内部
    const targetMore = e.target.closest('.more');
    if (targetMore) {
      targetMore.classList.toggle('open');
    }
  }, false);
})();

这种方法的优势:

  • 只需要绑定一次事件,性能更优
  • 后续动态添加的.more元素自动拥有点击功能,不需要重新绑定事件

内容的提问来源于stack exchange,提问作者vsoni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:14:25