
本文介绍一种无需 jquery 的轻量级方案:通过 vanilla JavaScript 监听滚动方向 + css position: sticky 分层控制,实现「向下滚动隐藏顶部横幅、向上滚动恢复显示」,同时确保主导航栏始终吸附顶部。
本文介绍一种无需 jquery 的轻量级方案:通过 vanilla javascript 监听滚动方向 + css `position: sticky` 分层控制,实现「向下滚动隐藏顶部横幅、向上滚动恢复显示」,同时确保主导航栏始终吸附顶部。
在现代网页中,智能头部(Smart Header)已成为提升用户体验的重要交互模式——它能减少视觉干扰、释放内容空间,又能在用户回溯意图明确时快速回归导航入口。本教程将基于纯 CSS 与原生 JavaScript,构建一个双层头部结构:上层为可折叠的「精简横幅(skinny banner)」,下层为主导航栏(main-nav)。关键要求是:
- ✅ 向下滚动时,横幅平滑隐藏;
- ✅ 向上滚动时,横幅平滑浮现;
- ✅ 主导航栏始终保持 sticky 状态,且在横幅隐藏后直接贴顶(无空白间隙);
- ❌ 不依赖 jQuery 或第三方库。
核心思路:分离粘性行为,用 js 控制显隐
CSS 的 position: sticky 本身不具备“滚动方向感知”能力,因此需结合 JavaScript 判断滚动趋势。但注意:不要给整个 .header-container 设置 sticky —— 这会导致横幅与导航被绑定为同一粘性单元,无法独立控制显隐。正确做法是:
- ✨ 仅对 .main-nav 应用 position: sticky; top: 0;,使其成为真正“永远钉在顶部”的容器;
- ✨ 将 .skinny-banner 设为普通文档流元素(或相对定位),通过 JS 动态切换其 transform: translateY() 或 opacity/height,配合 CSS transition 实现流畅动画。
完整实现代码
✅ HTML 结构(语义清晰,层级合理)
<div class="header-container"> <div class="skinny-banner">Skinny banner that should hide when scrolling down and appear when scrolling up</div> <div class="main-nav">Should always be visible and should pin to top when skinny banner is hidden</div> </div> <div class="dummy-content"></div>
✅ CSS 样式(重点:sticky 作用于 main-nav,横幅保留过渡)
body { margin: 0; } .skinny-banner { background-color: #2e7d32; /* 更规范的绿色 */ height: 40px; color: white; text-align: center; line-height: 40px; transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s ease; will-change: transform, opacity; } /* 向上滚动时显示,向下滚动时隐藏 */ .skinny-banner.hidden { transform: translateY(-100%); opacity: 0; } .main-nav { min-height: 112px; background-color: #000; color: white; text-align: center; line-height: 112px; position: sticky; top: 0; z-index: 100; /* 确保覆盖滚动内容 */ } .dummy-content { background-color: #fff9c4; height: 2000px; /* 增加高度便于测试滚动 */ padding: 2rem; }
✅ Vanilla JavaScript(轻量、防抖、方向判断)
let lastScrollTop = 0; const banner = document.querySelector('.skinny-banner'); const headerContainer = document.querySelector('.header-container'); // 使用 passive: true 提升滚动性能 window.addEventListener('scroll', () => { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; if (scrollTop > lastScrollTop && scrollTop > 60) { // 向下滚动且超过初始阈值(避免首页微动误触发) banner.classList.add('hidden'); } else if (scrollTop < lastScrollTop) { // 向上滚动 → 显示横幅 banner.classList.remove('hidden'); } lastScrollTop = scrollTop <= 0 ? 0 : scrollTop; // 防止负值 }, { passive: true }); // 可选:页面加载时初始化状态(如从锚点跳转后重置) window.addEventListener('load', () => { banner.classList.remove('hidden'); });
⚠️ 关键注意事项
- z-index 必须设置:.main-nav 的 z-index 需高于后续内容,否则滚动时可能被遮挡;
- will-change 提升性能:对 transform 和 opacity 添加 will-change,触发 GPU 加速;
- 滚动阈值(60px)建议保留:防止用户轻微滚动(如触摸屏惯性滑动)导致横幅频繁闪现;
- 避免 display: none:它会破坏文档流并中断 CSS 过渡,务必使用 transform + opacity 组合控制;
- 移动端兼容性:position: sticky 在 ios safari 15.4+ 和 android chrome 56+ 中已全面支持,旧版可添加渐进增强降级逻辑。
✅ 总结
该方案以“分治思想”解耦粘性与显隐逻辑:CSS 负责声明式布局约束(sticky on .main-nav),JavaScript 专注行为感知(滚动方向判定),两者协同达成丝滑体验。代码体积小、可维护性强,且完全符合现代 Web 性能最佳实践。你可根据项目需求进一步扩展,例如添加滚动节流、支持自定义触发距离、或集成 Intersection Observer 实现更精细的视口控制。