如何在高度不固定的sticky header下方实现固定菜单?
实现高度不固定的Sticky Header下方固定菜单
你当前的实现依赖header .top的固定高度来设置菜单偏移,但当header高度动态变化(比如banner内容调整、响应式布局)时,固定值会失效。下面提供两种可行的适配方案:
方案一:JS+CSS动态计算(无需调整HTML结构)
通过JS实时获取header的高度,动态设置菜单的位置和高度,同时监听可能导致header高度变化的事件,确保布局始终正确。
修改后的代码
HTML(结构不变)
<body> <div id="root"> <header> <div class="top"> Header <button type="button">Open</button> </div> <div class="banner"> Banner <!-- 内容可动态修改,header高度自动适配 --> </div> <nav>Menu <ul> <li>A</li> <li>B</li> <li>C</li> <li>D</li> </ul> </nav> </header> <main> <div> <p></p> <ul> <li>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</li> <li>Duis id sem non risus blandit convallis in ac ipsum.</li> <li>Mauris iaculis nisi at tellus aliquet vestibulum.</li> </ul> <p></p> <p></p> <ul> <li>Aenean varius nulla sit amet ex aliquam porttitor.</li> <li>Fusce ut sapien ut diam elementum elementum.</li> <li>Maecenas vehicula dui a consectetur tempor.</li> </ul> <p></p> <p></p> <ul> <li>Morbi nec ante efficitur, malesuada lectus nec, accumsan massa.</li> <li>Donec vitae arcu sed nunc tristique cursus id et turpis.</li> <li>Ut mattis ante dictum congue egestas.</li> <li>Ut a odio gravida, faucibus erat id, fermentum felis.</li> <li>Suspendisse vitae libero feugiat, iaculis augue et, vestibulum ex.</li> </ul> <p></p> <p></p> <ul> <li>Proin eleifend velit sit amet gravida tempor.</li> </ul> <p></p> <p></p> <ul> <li>Nam ut tortor ut leo posuere pharetra quis efficitur nisl.</li> <li>Suspendisse efficitur elit in lacus mollis, eu facilisis orci vulputate.</li> <li>Donec vulputate purus ut ligula euismod, eu venenatis tellus commodo.</li> <li>Praesent semper dui nec mattis lacinia.</li> <li>Duis dignissim justo quis rhoncus maximus.</li> </ul> <p></p> </div> </main> </div> </body>
CSS
#root { height: 200px; overflow-y: scroll; scrollbar-gutter: stable; position: relative; /* 让菜单绝对定位相对于root容器 */ } #root.hide-overflow { overflow-y: hidden; } header { position: sticky; top: 0; background: orange; z-index: 2; /* 确保header在菜单上方 */ } .banner { background: cyan; } nav { display: none; overscroll-behavior: contain; position: absolute; right: 0; width: 50%; background: green; z-index: 1; } nav.open { display: block; }
JavaScript
const root = document.getElementById('root'); const button = document.querySelector('button'); const nav = document.querySelector('nav'); const header = document.querySelector('header'); let open = false; // 计算并更新菜单的位置和高度 function updateMenuLayout() { if (!open) return; const headerHeight = header.offsetHeight; const rootHeight = root.offsetHeight; nav.style.top = `${headerHeight}px`; nav.style.height = `${rootHeight - headerHeight}px`; } // 切换菜单显示/隐藏 function toggleMenu() { open = !open; if (open) { button.innerText = 'Close'; nav.classList.add('open'); root.classList.add('hide-overflow'); updateMenuLayout(); // 打开时立即计算布局 } else { button.innerText = 'Open'; nav.classList.remove('open'); root.classList.remove('hide-overflow'); } } // 监听窗口大小变化和header内容更新 window.addEventListener('resize', updateMenuLayout); const headerObserver = new MutationObserver(updateMenuLayout); headerObserver.observe(header, { childList: true, subtree: true, attributes: true }); button.addEventListener('click', toggleMenu);
方案二:调整HTML结构+CSS Grid(简化逻辑)
如果允许调整HTML结构,将菜单移到root容器内(与header同级),可以用Grid布局配合CSS变量,减少JS代码量。
修改后的代码
HTML
<body> <div id="root"> <header> <div class="top"> Header <button type="button">Open</button> </div> <div class="banner"> Banner </div> </header> <!-- 将nav移到root下,与header、main同级 --> <nav>Menu <ul> <li>A</li> <li>B</li> <li>C</li> <li>D</li> </ul> </nav> <main> <!-- 内容不变 --> </main> </div> </body>
CSS
#root { height: 200px; overflow-y: scroll; scrollbar-gutter: stable; display: grid; grid-template-rows: auto 1fr; /* header高度自适应,main占剩余空间 */ position: relative; } header { position: sticky; top: 0; background: orange; z-index: 2; } .banner { background: cyan; } nav { display: none; overscroll-behavior: contain; position: absolute; right: 0; top: 0; height: 100%; width: 50%; background: green; z-index: 1; margin-top: var(--header-height); /* 通过JS设置header高度的CSS变量 */ } nav.open { display: block; }
JavaScript
const root = document.getElementById('root'); const button = document.querySelector('button'); const nav = document.querySelector('nav'); const header = document.querySelector('header'); let open = false; // 更新header高度的CSS变量 function updateHeaderHeight() { document.documentElement.style.setProperty('--header-height', `${header.offsetHeight}px`); } // 切换菜单显示/隐藏 function toggleMenu() { open = !open; if (open) { button.innerText = 'Close'; nav.classList.add('open'); root.classList.add('hide-overflow'); updateHeaderHeight(); } else { button.innerText = 'Open'; nav.classList.remove('open'); root.classList.remove('hide-overflow'); } } // 监听窗口大小变化和header内容更新 window.addEventListener('resize', updateHeaderHeight); const headerObserver = new MutationObserver(updateHeaderHeight); headerObserver.observe(header, { childList: true, subtree: true, attributes: true }); button.addEventListener('click', toggleMenu);
两种方案均能适配header高度不固定的场景:方案一无需修改HTML结构,兼容性更强;方案二结构更清晰,CSS逻辑更简洁,可根据实际需求选择。
内容的提问来源于stack exchange,提问作者steinybot
相关产品推荐
相关产品推荐

