嵌套ul导航菜单展开时最后一个li元素无法显示在菜单底部
问题根源
- 一级子菜单
.sub-1设置了overflow: visible,展开时写死的55.5px固定高度仅够容纳3个一级子项,嵌套的二级子菜单.sub-2展开后超出父容器的内容直接溢出、不占据正常文档流位置,必然会覆盖下方的最后一个列表项。 - 主菜单显隐逻辑写反:
.visible类配置的是visibility: hidden,且初始样式里visibility: 0是无效属性,visibility属性不接受数值参数。 - 写死固定高度的展开逻辑完全没考虑嵌套子菜单展开的高度增量,只要嵌套层级展开就会出现内容溢出错位。
修复方案
1. 替换错误CSS样式
把原有CSS替换为以下内容,核心是统一子菜单溢出隐藏规则、修正显隐逻辑、给菜单项加统一内边距保证高度计算准确:
* { box-sizing: border-box; } body { height: 100vh; padding: 0; margin: 0; } nav { height: 10vh; background-color: aquamarine; display: flex; justify-content: flex-end; align-items: center; position: relative; } #btn { position: absolute; top: 0; right: 10px; height: 10vh; display: flex; align-items: center; text-decoration: none; } .main { display: flex; position: absolute; flex-direction: column; list-style-type: none; padding-left: 0; align-items: center; top: 10vh; margin: 0; width: 100%; background: #fff; visibility: hidden; } .sub-1, .sub-2 { height: 0; list-style-type: none; padding-left: 0; display: flex; flex-direction: column; position: relative; overflow: hidden; visibility: hidden; transition: height 0.3s ease-out; align-items: center; } .height { visibility: visible; transition: height 0.3s ease; } .main.visible { visibility: visible; } li a { display: block; padding: 1rem 0; }
2. 替换原有JS逻辑
去掉写死高度的写法,改为自动计算子菜单实际高度,同时处理嵌套展开时父级菜单的高度联动,保证父容器能被子内容完全撑开:
const mainMenu = document.querySelector('.main'); const subMenu1 = document.querySelector('.sub-1'); const subMenu2 = document.querySelector('.sub-2'); document.querySelector('#btn').addEventListener('click', () => { mainMenu.classList.toggle('visible'); // 关闭主菜单时收起所有子菜单 subMenu1.classList.remove('height'); subMenu2.classList.remove('height'); subMenu1.style.height = ''; subMenu2.style.height = ''; }) document.querySelector('#submen-1').addEventListener('click', (e) => { e.stopPropagation(); const isOpen = subMenu1.classList.contains('height'); // 切换一级子菜单时先收起二级子菜单 subMenu2.classList.remove('height'); subMenu2.style.height = ''; if(isOpen) { subMenu1.style.height = `${subMenu1.scrollHeight}px`; requestAnimationFrame(() => { subMenu1.style.height = '0px'; }) subMenu1.classList.remove('height'); } else { subMenu1.classList.add('height'); subMenu1.style.height = `${subMenu1.scrollHeight}px`; subMenu1.addEventListener('transitionend', () => { subMenu1.style.height = 'auto'; }, {once: true}) } }) document.querySelector('#submen-2').addEventListener('click', (e) => { e.stopPropagation(); const isOpen = subMenu2.classList.contains('height'); // 切换二级子菜单前先锁定父级菜单当前高度,保证过渡效果正常 if(subMenu1.style.height === 'auto') { subMenu1.style.height = `${subMenu1.scrollHeight}px`; } if(isOpen) { subMenu2.style.height = `${subMenu2.scrollHeight}px`; requestAnimationFrame(() => { subMenu2.style.height = '0px'; }) subMenu2.classList.remove('height'); } else { subMenu2.classList.add('height'); subMenu2.style.height = `${subMenu2.scrollHeight}px`; } // 过渡完成后更新父级菜单高度,自动撑开位置 setTimeout(() => { subMenu1.style.height = 'auto'; subMenu1.style.height = `${subMenu1.scrollHeight}px`; }, 300) })
3. HTML结构无需修改
原有HTML的标签层级完全符合要求,不需要调整。
修复后所有子菜单展开时会自动撑开父容器高度,最后一个一级菜单项会随嵌套菜单的展开自动下移,不会被覆盖,同时保留平滑的展开收起过渡效果。
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

