David Díaz.

Scroll Spying for Fun and Profit

I recently redesigned by blog. One of the new features is a menu that highlights the current post you are on.

To achieve this, I used Zepto and a small amount of math. (The great thing about Zepto is that it’s fully compatible with jQuery, so this should work with jQuery out of the box as well).

Let’s jump right in, here’s how I do it:

$(window).on('scroll', function() {
    var scrollTop = $(window).scrollTop();
    var article_offsets = $('article').map(function(i) {
        if (i === 0) {
            return 0;
        }

        return $(this).offset().top
    });

    for (var i = 0; i < article_offsets.length; i++) {
        if (article_offsets[i] - 40 <= scrollTop) {
            $('.sidebar li')
                .removeClass('active')
                .eq(i)
                .addClass('active');
        }
    };
});

The first line of interest:

var article_offsets = $('article').map(function(i) {
    if (i === 0) {
        return 0;
    }

    return $(this).offset().top
});

Grabs all the vertical positions (from now on, offsets) of the elements I want to match and makes an array out of them. In this case, I am targeting the articles since, those represent my blog posts. I make sure to mark the first one as 0, so it works when the page first loads at scroll position 0.

After doing this, we iterate over all our scroll positions, if we find ourselves scrolled after one of the offsets we run this:

    $('.sidebar li').removeClass('active').eq(i).addClass('active');

This looks for the Ith element on the sidebar’s list and we add a class of ‘active’ to it, highlighting it and doing whatever we want with it.

There’s a couple of other (bigger, more complex and probably more robust) solutions to this problem out there. Here’s a couple I came through:

I am pretty sure they are plenty of others but, for my evil purposes, my tiny script fulfills my needs.

Have a comment? Feel free to email me.
Did you enjoy this post? Buy me a coffee ☕️.