如何优化多for循环实现点击切换元素类名的JavaScript代码?
嘿,作为JavaScript新手能写出功能正常的代码已经超棒了!咱们来一步步优化这段代码,让它更简洁、易维护,同时避开一些容易养成的不良编码习惯~
先说说现有代码可以优化的点
你的代码逻辑是对的,但存在几个可以改进的地方:
- 传统
for循环可读性不如现代的遍历方法 - 硬编码了
kenya相关的元素,以后新增目的地(比如Botswana)要重复写大量代码 - 给每个
to--kenya元素单独绑定事件,元素多的时候会浪费内存,且动态添加元素后需要重新绑定 - 全局变量太多,容易造成命名冲突
优化方案详解
1. 用forEach替代传统for循环
现代JavaScript中,NodeList(querySelectorAll返回的结果)已经支持forEach方法,不用再手动管理索引,代码更简洁易读:
// 替代原来的section移除循环 sections.forEach(section => section.classList.remove('active--section')); // 替代原来的导航链接移除循环 navLink.forEach(link => link.classList.remove('active--link'));
2. 抽象通用逻辑,避免硬编码
现在的代码只处理了Kenya的情况,我们可以从点击元素的类名中提取目的地名称(比如从to--kenya里拿到kenya),然后动态关联对应的section和导航链接,这样新增目的地时不用改核心逻辑:
// 提取目的地名称:从类名中找到以to--开头的类,截取后面的部分 const getDestination = (element) => { const toClass = [...element.classList].find(cls => cls.startsWith('to--')); return toClass ? toClass.replace('to--', '') : null; };
3. 使用事件委托减少事件监听
不用给每个to--kenya元素单独绑定点击事件,而是在它们的父元素(比如.main__wrapper)上绑定一次事件,通过事件冒泡判断点击的目标是否是我们需要的元素:
document.querySelector('.main__wrapper').addEventListener('click', (e) => { // 找到点击元素中最外层的to--xxx元素(防止点击子元素时出错) const target = e.target.closest('[class^="to--"]'); // 匹配所有以to--开头的类的元素 if (!target) return; // 如果点击的不是目标元素,直接返回 const destination = getDestination(target); if (!destination) return; // 1. 移除所有section的active--section类 sections.forEach(section => section.classList.remove('active--section')); // 2. 移除所有导航链接的active--link类 navLink.forEach(link => link.classList.remove('active--link')); // 3. 移除upNextKenya的active类 upNextKenya.classList.remove('up__next__box--active'); // 4. 给对应section添加active类 const targetSection = document.querySelector(`.${destination}`); if (targetSection) { targetSection.classList.add('active--section'); } // 5. 给对应导航链接添加active类 const targetNavLink = document.querySelector(`.navigation__link--${destination}`) || document.querySelector(`.to--${destination}`); if (targetNavLink) { targetNavLink.classList.add('active--link'); } });
4. 用const/let替代var
var存在变量提升和函数作用域的问题,现代JS推荐用const(值不变的变量)和let(值可变的变量):
// 原来的var改成const/let const sections = document.querySelectorAll('.sections'); const navLink = document.querySelectorAll('.navigation__link'); const upNextKenya = document.querySelector('.up__next--kenya'); // 那些单个元素的变量可以不用提前声明,需要时动态获取即可
完整优化后的代码
<main class="main__wrapper"> <!-- 你的HTML结构保持不变 --> <nav class="navigation"> <div class="navigation__inner"> <div class="navigation__logo"> <a href="#" class="navigation__logo--text">Safari</a> </div> <ul class="navigation__destinations"> <li class="navigation__destinations--item"> <a href="#" class="navigation__link to--home active--link">Home</a> </li> <li class="navigation__destinations--item"> <a href="#" class="navigation__link navigation__link--kenya to--kenya">Kenya</a> </li> <li class="navigation__destinations--item"> <a href="#" class="navigation__link to--botswana">Botswana</a> </li> <li class="navigation__destinations--item"> <a href="#" class="navigation__link to--south-africa">South Africa</a> </li> <li class="navigation__destinations--item"> <a href="#" class="navigation__link to--zambia"></a> </li> </ul> <ul class="navigation__list"> <li class="navigation__item"> <a href="#" class="navigation__link to--contact">Contact</a> </li> </ul> </div> </nav> <div class="up__next"> <div class="up__next__box up__next--kenya to--kenya up__next__box--active"> <div class="up__next__content"> <div class="up__next__info"> <h4 class="up__next__heading">Next</h4> <span class="up__next__text">Kenya - Maasai Mara National Reserve</span> </div> </div> <div class="up__next__img"> <div class="up__next__img--kenya"></div> </div> </div> </div> <section class="home sections active--section"> <div class="home__info__box"> <h2 class="heading__primary home__heading--primary">Heading</h2> <span class="home__info__box--text">text</span> </div> </section> <section class="kenya sections"> <div class="kenya__info__box"> <h2 class="heading__primary kenya__heading--primary">Masai Mara</h2> <span class="kenya__info__box--text">text</span> </div> </section> </main> <script> const sections = document.querySelectorAll('.sections'); const navLink = document.querySelectorAll('.navigation__link'); const upNextKenya = document.querySelector('.up__next--kenya'); // 提取目的地名称的工具函数 const getDestination = (element) => { const toClass = [...element.classList].find(cls => cls.startsWith('to--')); return toClass ? toClass.replace('to--', '') : null; }; // 事件委托处理点击 document.querySelector('.main__wrapper').addEventListener('click', (e) => { const target = e.target.closest('[class^="to--"]'); if (!target) return; const destination = getDestination(target); if (!destination) return; // 统一移除所有active类 sections.forEach(section => section.classList.remove('active--section')); navLink.forEach(link => link.classList.remove('active--link')); upNextKenya.classList.remove('up__next__box--active'); // 给对应元素添加active类 const targetSection = document.querySelector(`.${destination}`); const targetNavLink = document.querySelector(`.navigation__link--${destination}`) || document.querySelector(`.to--${destination}`); targetSection?.classList.add('active--section'); // 可选链操作符,避免null报错 targetNavLink?.classList.add('active--link'); }); </script>
额外的好习惯提醒
- 尽量减少全局变量:可以把所有逻辑封装在一个IIFE(立即执行函数)里,避免污染全局作用域
- 使用可选链操作符
?.:避免因为元素不存在而抛出错误(比如上面的targetSection?.classList) - 代码模块化:如果以后逻辑更复杂,可以把不同的功能拆分成单独的函数(比如移除所有active类的函数,添加active类的函数)
内容的提问来源于stack exchange,提问作者Liam
相关产品推荐
相关产品推荐

