Scrollspy
Scrollspy automatically highlights the nav link for whichever section of a long page is currently in view, updating as the user scrolls. It's the mechanism behind a "sticky side table of contents" that tracks reading position without any manual click handling. We'll build one for a TrailMap trail-guide page with an Overview/Difficulty/Directions/Reviews side-nav.
Setting Up Scrollspy: data-spy="scroll"
Scrollspy watches a scrollable container. Put data-spy="scroll" on that container, plus an id so the nav can reference it, and give it a fixed height with overflow-y: scroll in CSS (or spy on body for the whole page).
<body data-spy="scroll" data-target="#guideNav" data-offset="60"> ... </body>
data-bs-spy. Every attribute here — data-spy, data-target, data-offset — keeps the un-prefixed Bootstrap 4 naming.Pointing at the Nav: data-target
data-target on the spied container points at the id of the nav element whose links should get highlighted. Each link in that nav must have an href matching a section's id (e.g. "#overview").
<nav id="guideNav" class="nav flex-column">
<a class="nav-link" href="#overview">Overview</a>
<a class="nav-link" href="#difficulty">Difficulty</a>
</nav>
Requirements for Scrollspy to Work
A few things have to line up or nothing gets highlighted: each target section needs a real id, the nav links' href values must match those ids exactly (including the #), and the nav should use .nav-link items so Bootstrap knows which elements to toggle .active on. No initialization call is required beyond loading bootstrap.min.js — Scrollspy attaches itself automatically from the data-spy attribute on page load.
Worked Example: TrailMap Trail Guide Page With Sticky Side-Nav
A single trail guide page with four sections — Overview, Difficulty, Directions, Reviews — and a side-nav that stays highlighted on whichever section the hiker is currently reading.
<div class="row">
<!-- Sticky side-nav -->
<div class="col-md-3">
<nav id="guideNav" class="nav flex-column sticky-top" style="top: 70px;">
<a class="nav-link" href="#overview">Overview</a>
<a class="nav-link" href="#difficulty">Difficulty</a>
<a class="nav-link" href="#directions">Directions</a>
<a class="nav-link" href="#reviews">Reviews</a>
</nav>
</div>
<!-- Scrollable guide content, spied on -->
<div class="col-md-9" data-spy="scroll" data-target="#guideNav" data-offset="20"
style="height: 500px; overflow-y: scroll; position: relative;">
<h4 id="overview">Overview</h4>
<p>Ridgeline Loop is a 4.2 mi out-and-back with sweeping valley views from the ridge.</p>
<h4 id="difficulty">Difficulty</h4>
<p>Rated Moderate: 620 ft of elevation gain over switchbacks near the summit.</p>
<h4 id="directions">Directions</h4>
<p>Park at the north trailhead lot; the marked entrance is just past the ranger station.</p>
<h4 id="reviews">Reviews</h4>
<p>4.7 out of 5 from 132 hikers — most-praised for the summit viewpoint.</p>
</div>
</div>
As the hiker scrolls the right-hand column, Scrollspy watches which heading is currently near the top of the scrollable area and adds .active to the matching link in #guideNav — no click handlers, no manual JS.