Skip to content

Instantly share code, notes, and snippets.

@abalter
Last active December 11, 2024 22:23
Show Gist options
  • Save abalter/848900ba3ce6d74eb4419320d008360b to your computer and use it in GitHub Desktop.
Save abalter/848900ba3ce6d74eb4419320d008360b to your computer and use it in GitHub Desktop.
Very simple scrollspy
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>title</title>
<style type="text/css">
.nav {
position: fixed;
top: 0px;
}
.section {
height: 500px;
padding-top: 2em;
}
a {
color: black;
}
.active {
color: red;
}
</style>
</head>
<body>
<div class="nav">
<a class="link active" href="~/#one">one</a>
<a class="link" href="~/#two">two</a>
<a class="link" href="/#three">three</a>
<a class="link" href="/#four">four</a>
</div>
<div class="section" id="one">One</div>
<div class="section" id="two">Two</div>
<div class="section" id="three">Three</div>
<div class="section" id="four">Four</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
function spyOnScroll()
{
let anchors = $('a[href^="#"][class*="link"]');
let positions = [];
let hashes = [];
/*
Collect arrays of hashes and positions
with corresponding indices. Maybe some
fancier way, but it works.
*/
anchors.each(function(index, item)
{
let hash = item.hash;
let position = $(hash).position().top;
hashes.push(hash);
positions.push(position);
});
$(window).scroll(function()
{
let window_position = $(window).scrollTop();
let current_hash = window.location.hash;
/*
Find the target position that has the smallest
distance to the top of the current page.
The index of that position is the index of the
current page link.
Because of using the absolute value, the target
position can be above the top of the screen, or
in the screen. Hence, once the page top is between
two targets, the hash changes.
*/
let deltas = positions.map(x => Math.abs(
x - window_position
));
let min = Math.min(...deltas);
let index = deltas.indexOf(min);
let new_hash = hashes[index];
/*
Set active menu item.
*/
$('.nav a[href="' + new_hash + '"]').addClass('active'); // make link active
$('.nav a[href!="' + new_hash + '"]').removeClass('active'); //make all others not active
/*
No need to push every scroll to the history. Just when the hash changes.
*/
if (new_hash != current_hash)
{
history.pushState(null, null, new_hash);
}
});
}
$(window).on('load', spyOnScroll);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment