如何阻止项目中侧边栏菜单的li元素随选中指示器移动?
问题解答
为什么li元素会跟着移动?
你把指示器(.menu-sdbr-list-indicator)作为<ul>的第一个子li元素,并且用margin-bottom的负数值来移动它。在文档流中,元素的margin会直接影响相邻元素的位置——负的margin-bottom会让后续的兄弟元素向上偏移,再加上<ul>是flex布局,子元素的间距和位置会被margin直接影响,所以调整指示器的margin-bottom时,后面所有li都会跟着移动。
修复与优化方案
核心修复思路
将指示器脱离文档流,用绝对定位固定在<ul>容器内,这样它的移动不会影响其他菜单元素的位置。
代码优化点
- 移除重复的
id属性(多个li用同一个id不符合HTML规范) - 用CSS类控制选中状态和指示器位置,替代直接设置
style属性,避免代码冗余 - 用
transform: translateY()替代margin做动画,性能更优 - 简化JS逻辑,减少重复DOM操作
优化后的完整代码
HTML
<nav class="menu-sidebar-overlay"> <div class="menu-sidebar"> <div class="menu-sdbr-contents"> <h1 class="menu-sdbr-title">MENU</h1> <ul class="menu-sdbr-list" id="menu-sdbr-list"> <!-- 指示器改为绝对定位,不再参与文档流布局 --> <li class="menu-sdbr-list-indicator" id="menu-sdbr-list-indicator"></li> <li class="menu-sdbr-list-item"> <button> <span class="material-icons">dark_mode</span> Dark Mode </button> </li> <li class="menu-sdbr-list-item"> <button> <span class="material-icons">settings</span> Settings </button> </li> <li class="menu-sdbr-list-item"> <button> <span class="material-icons">logout</span> Log Out </button> </li> </ul> </div> <div class="rectangle"></div> </div> </nav>
CSS
@import url('https://fonts.googleapis.com/css2?family=Fira+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); @import "https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Round"; body { --color-primary-100: rgb(105, 54, 245); --color-primary-200: rgb(128, 78, 247); --color-primary-300: rgb(148, 100, 249); --color-primary-400: rgb(166, 122, 251); --color-primary-500: rgb(183, 144, 252); --color-primary-600: rgb(199, 165, 253); --color-surface-100: rgb(0, 0, 0); --color-surface-200: rgb(30, 30, 30); --color-surface-300: rgb(53, 53, 53); --color-surface-400: rgb(78, 78, 78); --color-surface-500: rgb(105, 105, 105); --color-surface-600: rgb(133, 133, 133); --color-surface-mixed-100: rgb(26, 22, 37); --color-surface-mixed-200: rgb(40, 35, 48); --color-surface-mixed-300: rgb(63, 58, 70); --color-surface-mixed-400: rgb(88, 83, 94); --color-surface-mixed-500: rgb(113, 109, 119); --color-surface-mixed-600: rgb(140, 136, 144); --color-primary-100-mix: 105, 54, 245; --color-primary-500-mix: 183, 144, 252; font-family: Fira Sans Condensed, Arial; margin: 0; } .menu-sidebar-overlay { position: fixed; background-color: rgba(0, 0, 0, 0.5); z-index: 200; width: 100%; height: 100%; display: flex; align-items: center; justify-content: flex-end; color: white; } .menu-sidebar { position: relative; height: 100%; width: 35vw; background-color: var(--color-primary-100); display: flex; } .menu-sidebar .rectangle { width: 15%; height: 100%; background-color: var(--color-surface-300); } .menu-sdbr-title { display: inline; color: var(--color-primary-200); font-size: 40px; margin: 15px 0 0 10px; color: white; } .menu-sdbr-contents { width: 100%; position: relative; height: 100%; flex: 1; display: flex; flex-direction: column; margin-left: 10px; } .menu-sdbr-list { position: absolute; width: 100%; padding: 0; margin: 90px 0 0 0; list-style-type: none; display: flex; flex-direction: column; gap: 5px; height: auto; } /* 修改指示器定位,脱离文档流 */ .menu-sdbr-list-indicator { position: absolute; top: 0; left: 0; width: 100%; height: 60px; background-color: var(--color-surface-300); z-index: 250; border-top-left-radius: 10px; border-bottom-left-radius: 10px; transition: transform 150ms ease; } .menu-sdbr-list-item button { position: relative; background-color: transparent; border: 0; width: 100%; padding: 0 15px; height: 60px; text-align: left; display: flex; align-items: center; color: var(--color-surface-300); z-index: 300; border-top-left-radius: 10px; border-bottom-left-radius: 10px; font-size: 15px; font-weight: 400; transition: color 150ms; } /* 按钮选中状态类 */ .menu-sdbr-list-item button.active { color: white; } .menu-sdbr-list-item button .material-icons { margin-right: 15px; font-size: 20px; }
JavaScript
const menuButtonElements = document.querySelectorAll('.menu-sdbr-list-item button'); const menuIndicatorElement = document.getElementById('menu-sdbr-list-indicator'); const menuItemHeight = 65; // 菜单项高度+gap(60px+5px) menuButtonElements.forEach((btn, index) => { btn.addEventListener('click', () => { // 重置所有按钮选中状态 menuButtonElements.forEach(b => b.classList.remove('active')); // 标记当前按钮为选中 btn.classList.add('active'); // 移动指示器,用transform实现平滑动画 menuIndicatorElement.style.transform = `translateY(${index * menuItemHeight}px)`; }); }); // 初始化第一个按钮为选中状态 menuButtonElements[0].classList.add('active');
内容的提问来源于stack exchange,提问作者CindyTheCat
相关产品推荐
相关产品推荐

