You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scrollspy垂直侧边栏菜单:滚动时保持当前激活项可视的方案求解

垂直Scrollspy菜单解决方案

问题说明

开发垂直Scrollspy菜单时遇到以下问题:

  • 页面滚动时,侧边栏导航无法自动滚动,章节较多时看不到当前激活的导航项
  • 尝试Bootstrap Scrollspy组件未生效

核心修复点

1. 让导航跟随激活项滚动

修改导航的CSS样式,使其支持垂直滚动,并在激活项变化时自动滚动到可视区域:

  • 将导航的overflow-x: scroll改为overflow-y: auto,并设置height: 100vh使其占满视口高度
  • 在激活导航项时,调用scrollIntoView()方法让激活项滚动到导航的可视区域内

2. 优化Scrollspy逻辑

原代码存在循环覆盖、判断不准确的问题,优化后:

  • 使用Array.from(sections).find()找到当前处于视口的章节,避免循环重复操作
  • 用getBoundingClientRect()更准确判断章节是否在视口内
  • 移除冗余的最后章节判断逻辑,统一视口判断规则

完整代码示例

HTML

<nav>
  <a href="#section-1" class="active">Section 1</a>
  <a href="#section-2">Section 2</a>
  <a href="#section-3">Section 3</a>
  <a href="#section-4">Section 4</a>
  <a href="#section-5">Section 5</a>
  <a href="#section-6">Section 6</a>
  <a href="#section-7">Section 7</a>
  <a href="#section-8">Section 8</a>
  <a href="#section-9">Section 9</a>
  <a href="#section-10">Section 10</a>
</nav>

<div class="section-container" tabindex="0">
  <section id="section-1">Section 1 <p>(200px height)</p>
  </section>
  <section id="section-2">Section 2 <p>(800px height)</p>
  </section>
  <section id="section-3">Section 3 <p>(400px height)</p>
  </section>
  <section id="section-4">Section 4 <p>(1200px height)</p>
  </section>
  <section id="section-5">Section 5 <p>(600px height)</p>
  </section>
  <section id="section-6">Section 6 <p>(600px height)</p>
  </section>
  <section id="section-7">Section 7 <p>(600px height)</p>
  </section>
  <section id="section-8">Section 8 <p>(600px height)</p>
  </section>
  <section id="section-9">Section 9 <p>(600px height)</p>
  </section>
  <section id="section-10">Section 10 <p>(600px height)</p>
  </section>
</div>

CSS

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  display: flex;
  position: relative;
}

html {
  scroll-behavior: smooth;
  overflow-y: scroll;
  height: 100vh;
}

nav {
  display: flex;
  position: fixed;
  flex-direction: column;
  top: 0;
  width: 20%;
  height: 100vh; /* 占满视口高度 */
  overflow-y: auto; /* 垂直滚动 */
}

nav a {
  padding: 10px 25px;
  color: black;
  font-size: 20px;
  text-transform: capitalize;
  background-color: lightgray;
  font-family: sans-serif;
  text-align: center;
  text-decoration: none;
}

nav a.active,
nav a:hover {
  background-color: darkgray;
}

section {
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  text-transform: uppercase;
  font-size: 7rem;
  font-family: sans-serif;
  border: 1px solid gray;
  flex-direction: column;
}

section p {
  font-size: 1rem;
  font-weight: 300;
}

section:nth-child(odd) {
  background-color: #eee;
}

.section-container {
  width: 80%;
  margin-left: auto;
}

#section-1 { height: 200px; }
#section-2 { height: 800px; }
#section-3 { height: 400px; }
#section-4 { height: 1200px; }
#section-5 { height: 600px; }
#section-6 { height: 600px; }
#section-7 { height: 600px; }
#section-8 { height: 600px; }
#section-9 { height: 600px; }
#section-10 { height: 600px; }

JavaScript

const sections = document.querySelectorAll("section");
const navLinks = document.querySelectorAll("nav a");
const nav = document.querySelector("nav");

window.addEventListener('scroll', () => {
  // 找到当前处于视口的章节(顶部进入视口1/3位置时激活)
  const currentSection = Array.from(sections).find(section => {
    const rect = section.getBoundingClientRect();
    // 当章节顶部进入视口下方1/3位置时触发激活
    return rect.top <= window.innerHeight / 3 && rect.bottom >= window.innerHeight / 3;
  });

  if (currentSection) {
    const id = currentSection.getAttribute('id');
    // 移除所有激活类
    navLinks.forEach(link => link.classList.remove('active'));
    // 给对应导航项添加激活类
    const activeLink = document.querySelector(`nav a[href="#${id}"]`);
    activeLink.classList.add('active');
    // 让激活项滚动到导航可视区域
    activeLink.scrollIntoView({ block: 'center', behavior: 'smooth' });
  }
});

关键说明

  • 导航设置为height:100vh和overflow-y:auto,保证在章节过多时可以内部滚动
  • 使用getBoundingClientRect()判断章节位置,相比offsetTop更准确适配不同布局
  • 激活项滚动时使用scrollIntoView({block:'center'}),确保激活项始终出现在导航可视区域中间
  • 优化后的Scrollspy逻辑只处理当前激活的章节,避免不必要的循环操作

内容的提问来源于stack exchange,提问作者Jolie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 07:27:22