正如标题所说!这是一个侧边栏导航栏,它……
- 使用粘性定位。它尽可能地停留在屏幕上,但不会与页眉、页脚重叠,也不会使任何链接无法访问。
- 平滑滚动到您点击的章节。
- 根据滚动位置激活当前导航(这是一个单页面内容)。
查看 CodePen 上 Chris Coyier (@chriscoyier) 编写的笔 粘性、平滑、激活导航。
粘性
很容易在某个元素上添加 position: sticky; top: 0;
。但是为了使其正常工作,它必须位于一个更高的父元素内。因此,导航 (<nav>
) 内的无序列表 (<ul>
) 在这里非常有效。由于 CSS 网格布局,<nav>
与 <main>
内容区域一样高。但是,请注意,我们还必须为 iOS 使用 position: -webkit-sticky;
。
我还为垂直媒体查询添加了一个 神奇数字,以便它不会以无法访问较低导航项的方式粘贴
/* Only stick if you can fit */
@media (min-height: 300px) {
nav ul {
position: sticky;
top: 0;
}
}

平滑
在我第一次尝试这个的时候,我考虑过基于 JavaScript 的 平滑滚动。如今它甚至已成为原生功能,无需框架。您可以定位一个元素并平滑地滚动到它
document.querySelector('.hello').scrollIntoView({
behavior: 'smooth'
});
将其应用于任意一组导航……
let mainNavLinks = document.querySelectorAll("nav ul li a");
mainNavLinks.forEach(link => {
link.addEventListener("click", event => {
event.preventDefault();
let target = document.querySelector(event.target.hash);
target.scrollIntoView({
behavior: "smooth",
block: "start"
});
});
});
Chrome 和 Firefox 都支持,但 Edge 和 Safari 不支持。
然后我突然想到,CSS 可以做到这一点!有一个 scroll-behavior
属性,您可以将其放在文档上以使所有内容都以这种方式滚动
html {
scroll-behavior: smooth;
}
由于我们的导航 <a>
链接是哈希/跳转/锚链接,因此这正是我们所需要的。忘记 JavaScript 吧。特别是由于 scroll-behavior
的浏览器支持 与 .scrollIntoView()
的“平滑”版本相同。
激活
这有点棘手,特别是因为这是一个单页面滚动应用程序,而不是具有自己独立文档的各个页面。如果它们是单独的文档,我们将在导航中的某个位置更改活动类,或者使用 body.specific_page
类或类似的东西。
相反,我们需要查看页面的滚动位置,确定哪个部分处于视野中并以此方式进行标记。可能有一些花哨的 IntersectionObserver
方法可以处理这个问题,但我无法完全理解它,因此我仅仅查看所有相关的部分,进行一些测量和计算,并以此方式确定链接是否处于活动状态。
let mainNavLinks = document.querySelectorAll("nav ul li a");
let mainSections = document.querySelectorAll("main section");
let lastId;
let cur = [];
window.addEventListener("scroll", event => {
let fromTop = window.scrollY;
mainNavLinks.forEach(link => {
let section = document.querySelector(link.hash);
if (
section.offsetTop <= fromTop &&
section.offsetTop + section.offsetHeight > fromTop
) {
link.classList.add("current");
} else {
link.classList.remove("current");
}
});
});
那里的滚动处理程序应该触发一个小警告标志。这是一种可能应该 节流 的事情,例如,如果您有 lodash 可用
window.addEventListener("scroll", () => {
_.throttle(doThatStuff, 100);
});
我只是在这里没有这样做,以保持演示的无依赖性。
哦!而且它在移动设备上(这里的 iOS)基本运行良好

JavaScript 库主页的免费模板
我在 我制作的这个模板中使用了所有这些内容,您可以随意使用。

您是否考虑过使用 IntersectionObserver?(从性能角度来看非常有趣)
我测试了它来制作 RevealJS 的简单克隆(使用 CSS 滚动捕捉点和 ScrollIntoView),请参阅 https://medium.com/@Nico3333fr/creating-a-light-revealjs-clone-with-css-scroll-snap-points-306dfba71652
我也想说同样的话!我还在我的 LazyLoad 中使用 IntersectionObserver,它运行得非常完美!监听窗口滚动事件很繁重,可以将其用作后备方案。
我在 Firefox 62dev 中发现了一个奇怪的错误(我称之为“错误”,因为我不理解它)。
导致
nav ul
元素不粘性。从 body 中删除该声明可以解决此问题。啊,是的,很有趣!我不确定哪个浏览器做对了,但它似乎并非完全必要,因此我暂时将其删除并在代码中进行了说明。
当然!这已在笔的 CSS 注释中进行了说明。 :)
似乎 Bootstrap 很久以前就使用 scrollspy 和 affix 做到了这一点。
您可能需要尝试硬刷新。在我最新的稳定版本 (61.0.1) 中似乎可以正常工作。
我曾经认为使用 jQuery 会导致代码变成意大利面条。
但在过去的几年里,从 Angular 到 Vue,现在又回到 jQuery……我意识到是缺乏约定导致了意大利面条代码。
您的演示代码缺乏 BEM 和元素链式选择器的使用,这对新兴的开发人员造成了不良习惯。
反而
现在您已将 js 行为与 css 分离,修改案例名称将不需要更改 js。
修改 HTML 结构不会破坏 js。
也很明显该元素是某个事物的一部分。
这些都是您在大型团队中担任前端开发人员时必须处理的事情,您希望最大程度地减少其他团队成员的维护工作量。
请记住,简单和容易是两个完全不同的结果。
我修改了笔(快速且粗略,它有很多错误)以展示如何使用交叉观察器来完成,它还没有正常工作(在全屏模式下它看起来像是在工作,但如果您拉伸视口,它就不会工作)。
在较高的浏览器窗口中,我无法导航到最后一节。
一个可以提供帮助的调整是向
scroll
事件监听器添加{passive: true}
,因为您的处理程序永远不会取消scroll
事件。它 得到广泛支持,并且具有 性能优势。您可以使用一个简单的节流函数来实现目的,而无需框架/库。
然后使用以下命令调用它
““
window.addEventListener(“scroll”, throttled(() => {
…
}));
“`