JavaScript手风琴功能无法支持多实例正常触发问题求解
问题根因
不同手风琴实例的aria-controls和对应的aria-labelledby属性值重复。document.querySelector默认会返回整个文档中第一个匹配的元素,因此点击第二个及以后手风琴的按钮时,查询到的目标折叠面板仍然是第一个手风琴内的对应元素,就会出现点击无响应、误触发第一个手风琴展开的问题。
解决方案
第一步:修正HTML属性,保证全局唯一
按照HTML规范,所有aria-controls和对应的aria-labelledby属性值必须全局唯一,给不同手风琴的属性值加唯一前缀/后缀区分即可,示例如下:
<!-- 第一个手风琴 --> <nav class="accordion-navigation"> <h3>Accordion 1</h3> <div class="accordion-navigation__title accordion__title"> <button class="accordion__toggle" aria-controls="accordion-1-nav-1" aria-expanded="false" data-accordion-header=""></button> Level 0 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M-3.49691e-07 8L1.41 9.41L7 3.83L7 16L9 16L9 3.83L14.59 9.41L16 8L8 -3.49691e-07L-3.49691e-07 8Z" fill="white"></path> </svg> </div> <ol class="accordion-navigation__list" aria-labelledby="accordion-1-nav-1" hidden=""> <li aria-current="true"> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> </ol> <div class="accordion-navigation__title accordion__title"> <button class="accordion__toggle" aria-controls="accordion-1-nav-2" aria-expanded="false" data-accordion-header=""></button> Level 0 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M-3.49691e-07 8L1.41 9.41L7 3.83L7 16L9 16L9 3.83L14.59 9.41L16 8L8 -3.49691e-07L-3.49691e-07 8Z" fill="white"></path> </svg> </div> <ol class="accordion-navigation__list" aria-labelledby="accordion-1-nav-2" hidden=""> <li aria-current="true"> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> </ol> </nav> <!-- 第二个手风琴 --> <nav class="accordion-navigation"> <h3>Accordion 2</h3> <div class="accordion-navigation__title accordion__title"> <button class="accordion__toggle" aria-controls="accordion-2-nav-1" aria-expanded="false" data-accordion-header=""></button> Level 0 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M-3.49691e-07 8L1.41 9.41L7 3.83L7 16L9 16L9 3.83L14.59 9.41L16 8L8 -3.49691e-07L-3.49691e-07 8Z" fill="white"></path> </svg> </div> <ol class="accordion-navigation__list" aria-labelledby="accordion-2-nav-1" hidden=""> <li aria-current="true"> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> </ol> <div class="accordion-navigation__title accordion__title"> <button class="accordion__toggle" aria-controls="accordion-2-nav-2" aria-expanded="false" data-accordion-header=""></button> Level 0 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M-3.49691e-07 8L1.41 9.41L7 3.83L7 16L9 16L9 3.83L14.59 9.41L16 8L8 -3.49691e-07L-3.49691e-07 8Z" fill="white"></path> </svg> </div> <ol class="accordion-navigation__list" aria-labelledby="accordion-2-nav-2" hidden=""> <li aria-current="true"> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> <li> <a href="#_">Level 1</a> </li> </ol> </nav>
第二步(可选优化):增强JS逻辑健壮性
可以调整查询逻辑,从当前按钮所属的手风琴容器内查询目标元素,进一步避免全局重复导致的问题:
const headers = document.querySelectorAll('[data-accordion-header]'); headers.forEach(header => { const identifier = header.getAttribute("aria-controls"); // 先获取当前按钮所属的手风琴容器,再在容器内查询目标元素 const accordionWrapper = header.closest('.accordion-navigation'); const target = accordionWrapper.querySelector(`[aria-labelledby="${identifier}"]`); header.addEventListener("click", event => { event.preventDefault(); const expanded = header.getAttribute("aria-expanded") === "true"; header.setAttribute("aria-expanded", !expanded); target.hidden = expanded; }); });
调整后只要单个手风琴内的aria-controls和aria-labelledby对应正确,就算不同手风琴之间的属性值重复,也能正常运行。
内容的提问来源于stack exchange,提问作者user173420
相关产品推荐
相关产品推荐

