如何用jQuery关闭多级菜单中当前点击分支外的展开菜单?
多级菜单jQuery开合逻辑问题
我正在用CSS Flex制作多级菜单,通过jQuery实现菜单及子菜单的开合功能,需求逻辑如下:
- 点击「Contact」展开其菜单
- 点击「A propo」时,「Contact」需关闭
- 点击一级子菜单(Item 1.2),父级菜单保持展开
- 点击二级子菜单(Item 1.2.3),其所有直接父级ul不关闭
- 再次点击「Contact」时,「A propo」的所有菜单及子菜单需关闭
当前开合菜单的脚本可正常运行,但缺少关闭其他分支的逻辑:
( function( $ ) { $( '.dropdown-toggle' ).click( function( e ) { var _this = $( this ); e.preventDefault(); _this.toggleClass( 'toggled-on' ); _this.find('.fa').toggleClass( 'fa-angle-down fa-angle-up'); _this.parent().next( '.sub-menu' ).toggleClass( 'toggled-on' ); _this.attr( 'aria-expanded', _this.attr( 'aria-expanded' ) === 'false' ? 'true' : 'false' ); // 需添加关闭其他菜单的逻辑 } ); })( jQuery );
我遇到的具体疑问:
- 如何从
_this定位到顶级的li.hasChildren? - 如何检查顶级li之外的其他
li.hasChildren分支? - 如何关闭当前点击分支外的所有
ul.toggled-on元素?
我试过用parentUntil()方法,但它遇到第一个li.hasChildren就停止了,无法定位到顶级li。注:菜单展开时对应ul会添加ul.toggled-on类,关闭时需调用removeClass('toggled-on')。
菜单的HTML结构如下:
<nav class="e-panel e-panel-row" role="navigation" aria-label="Main navigation"> <ul class="e-justify-content-space-beween bg-black"> <li class="hasChildren"> <div> <a href="#">A propo</a> <a href="#" class="dropdown-toggle" aria-expanded="false"> [12] <i class="fa fa-angle-down"></i> </a> </div> <ul class="sub-menu" role="group"> <li> <div> <a class="link" href="#">Item 1.1</a> </div> </li> <li class="hasChildren"> <div> <a href="#">Item 1.2</a> <a href="#" class="dropdown-toggle" aria-expanded="false"> [1] <i class="fa fa-angle-down"></i> </a> </div> <ul class="sub-menu" role="group"> <li> <div> <a href="#">Item 1.2.1</a> </div> </li> <li> <div> <a href="#">Item 1.2.2</a> </div> </li> <li class="hasChildren"> <div> <a href="#">Item 1.2.3</a> <a href="#" class="dropdown-toggle" aria-expanded="false"> [1] <i class="fa fa-angle-down"></i> </a> </div> <ul class="sub-menu"> <li><div><a href="#">Item 1.2.3.1</a></div></li> <li><div><a href="#">Item 1.2.3.2</a></div></li> <li><div><a href="#">Item 1.2.3.3</a></div></li> </ul> </li> </ul> </li> <li> <div> <a href="#">Item 333333</a> </div> </li> </ul> </li> <li> <div> <a href="#">Team</a> </div> </li> <li class="hasChildren"> <div> <a href="#">Contact</a> <a href="#" class="dropdown-toggle" aria-expanded="false"> [13] <i class="fa fa-angle-down"></i> </a> </div> <ul class="sub-menu" role="group"> <li> <div> <a href="#">Adresse</a> </div> </li> <li> <div> <a href="#">Téléphone</a> </div> </li> <li> <div> <a href="#">e-mail</a> </div> </li> </ul> </li> <li> <div> <a href="#">Nos cours</a> </div> </li> <li> <div> <a href="#">Menu 3</a> </div> </li> <li> <div> <a href="#">Encore un beau menu 4</a> </div> </li> <li> <div> <a href="#">Contact</a> </div> </li> </ul> </nav>
核心问题:如何筛选并关闭当前点击分支外的所有ul.toggled-on元素?
解决方案
要实现需求,我们可以按以下步骤修改代码:
- 定位当前分支的所有父级及自身菜单:从点击的
dropdown-toggle出发,找到所有属于当前分支的ul.sub-menu和对应的dropdown-toggle按钮。 - 关闭其他分支的所有展开项:选中所有
ul.toggled-on,排除当前分支内的元素,然后统一关闭,并重置对应的按钮状态。
修改后的完整代码如下:
( function( $ ) { $( '.dropdown-toggle' ).click( function( e ) { var _this = $( this ); e.preventDefault(); // 处理当前菜单的开合状态 var currentSubmenu = _this.parent().next( '.sub-menu' ); var isExpanding = !_this.hasClass( 'toggled-on' ); _this.toggleClass( 'toggled-on' ); _this.find('.fa').toggleClass( 'fa-angle-down fa-angle-up'); currentSubmenu.toggleClass( 'toggled-on' ); _this.attr( 'aria-expanded', isExpanding ? 'true' : 'false' ); // 获取当前分支的所有菜单元素(包括父级子菜单和当前子菜单) var currentBranch = _this.parentsUntil( '.e-justify-content-space-beween', '.sub-menu' ) .addBack() .add( currentSubmenu ); // 关闭所有非当前分支的展开菜单 $('.sub-menu.toggled-on').not(currentBranch).each(function() { var $submenu = $(this); $submenu.removeClass('toggled-on'); // 重置对应toggle按钮的状态 var $toggle = $submenu.prev('div').find('.dropdown-toggle'); $toggle.removeClass('toggled-on'); $toggle.find('.fa').removeClass('fa-angle-up').addClass('fa-angle-down'); $toggle.attr('aria-expanded', 'false'); }); // 如果是收起顶级菜单,同时关闭其下所有子菜单 var topLevelLi = _this.closest('.hasChildren'); if (!isExpanding && topLevelLi.parent().hasClass('e-justify-content-space-beween')) { topLevelLi.find('.sub-menu.toggled-on').removeClass('toggled-on'); topLevelLi.find('.dropdown-toggle.toggled-on').each(function() { var $toggle = $(this); $toggle.removeClass('toggled-on'); $toggle.find('.fa').removeClass('fa-angle-up').addClass('fa-angle-down'); $toggle.attr('aria-expanded', 'false'); }); } } ); })( jQuery );
代码说明:
- 定位当前分支:用
parentsUntil('.e-justify-content-space-beween', '.sub-menu')找到当前点击按钮所在的所有父级子菜单,结合当前子菜单组成当前分支的元素集合,避免误关同分支内的菜单。 - 关闭其他分支:通过
.not(currentBranch)排除当前分支,遍历剩余展开菜单,统一重置类和属性状态。 - 顶级菜单收起逻辑:判断当前点击的是否为顶级菜单按钮,若为收起操作,同时关闭其下所有展开的子菜单,满足需求中的第5点。
内容的提问来源于stack exchange,提问作者pierrot10
相关产品推荐
相关产品推荐

