jQuery嵌套hover事件mouseleave移除类问题:保持子菜单始终有.active类
多级菜单交互逻辑优化
原始结构与现有问题
HTML结构
<ul class="menu-wrapper"> <li> <a href="#">Menu item</a> <ul class="sub-menu"> <li> <a href="#">Subitem-1</a> <ul> <li>Sub-Subitem-1</li> <li>Sub-Subitem-2</li> <li>Sub-Subitem-3</li> </ul> </li> <li> <a href="#">Subitem-2</a> <ul> <li>Sub-Subitem-1</li> <li>Sub-Subitem-2</li> <li>Sub-Subitem-3</li> </ul> </li> </ul> </li> </ul>
现有jQuery实现
$('ul.menu-wrapper').children().hover( function() { let subMenu = $(this).find('ul.sub-menu').first() subMenu.addClass('active') subMenu.children().first().addClass('active') }, function() { let subMenu = $(this).find('ul.sub-menu').first() subMenu.removeClass('active') } )
需求与痛点
- 新增功能:悬停
ul.sub-menu内的li时,将.active类切换到当前悬停项 - 修复问题:当前mouseleave事件会在鼠标从子菜单返回父菜单项时移除子菜单的
.active类,导致子菜单内无激活项,需要保证此时子菜单内始终至少有一个li带.active类
优化后的实现代码
// 获取父菜单项 const parentMenuItem = $('ul.menu-wrapper').children('li'); // 父菜单项鼠标进入事件 parentMenuItem.on('mouseenter', function() { const subMenu = $(this).find('ul.sub-menu').first(); // 仅在子菜单未激活时执行初始化 if (!subMenu.hasClass('active')) { subMenu.addClass('active'); // 优先保留之前的激活子项,无则默认选中第一个 const activeSubItem = subMenu.find('li.active').first(); if (activeSubItem.length === 0) { subMenu.children('li').first().addClass('active'); } } }); // 子菜单项鼠标进入事件:切换激活状态 $('ul.sub-menu li').on('mouseenter', function() { // 移除兄弟项的active类 $(this).siblings().removeClass('active'); // 给当前项添加active类 $(this).addClass('active'); }); // 父菜单项鼠标离开事件:仅完全离开父项区域时移除激活 parentMenuItem.on('mouseleave', function(e) { // 检查鼠标是否移动到子菜单内,不是则移除所有激活类 if (!$(e.relatedTarget).closest('.sub-menu').length) { const subMenu = $(this).find('ul.sub-menu').first(); subMenu.removeClass('active'); subMenu.find('li.active').removeClass('active'); } });
逻辑说明
- 父项mouseenter优化:仅在子菜单未激活时执行初始化,同时优先保留之前的激活子项,没有则默认选中第一个,保证返回父项时子菜单有激活项
- 子项mouseenter处理:切换激活状态,移除兄弟项的
.active,给当前项添加,实现悬停切换 - 父项mouseleave优化:通过
e.relatedTarget判断鼠标是否移动到子菜单内,只有完全离开父项区域(包括子菜单)时才移除所有激活类,避免鼠标在父项和子菜单之间移动时误移除
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

