header

Simple Infinite Scrolling

While looking at the demo site of Twenty Fifteen (the theme I use for this blog), I noticed it used infinite scrolling, where more articles get automatically loaded when you reach the end of the page. It looked like a neat thing to have, but since I didn’t actually use WordPress, I couldn’t use the plugins that provide infinite scrolling. Writing it myself seemed like more effort than it was worth, so I dismissed it at the time. However, I later realized that I could actually do it with just a few lines of client-side JavaScript, and didn’t need any changes or support from the backend (or, in my case, static site generator) or the theme.

First the boring stuff: when the blog front page (containing the first 10 items) has loaded, you first need to get the link to the next page of items. In Twenty Fifteen, all the links to the blog pages are located in the navigation bar at the bottom of every page, so it’s just a matter of finding the current page number and getting the next link:

// Extracts the URL of the next page from the navigation bar
function getNextPageURL($navContainer) {
  return $navContainer
    .find('nav .current')
    .next('a').attr('href');
}

// Initialize the 'next page' URL
var nextPageURL = getNextPageURL($('main'));

Initially, this will make nextPageUrl point to /blog/page/2. You could hard-code the URL scheme, but extracting it from the page makes it a bit more robust against future changes.

When the visitor scrolls to the bottom of the page, we want to add extra items to the list of blog items. So, we add a scroll event listener, wait for the navigation bar to come into sight, and then trigger an event to load the next page and add its items:

// Checks if an element is only a page scroll away
function isAlmostVisible($el) {
  var windowHeight = $(window).height();
  var windowTop = $(window).scrollTop();
  var windowBottom = windowTop + windowHeight;
  var elTop = $el.offset().top;
  var elBottom = elTop + $el.height();
  return (elTop >= windowTop 
      && elTop <= windowBottom + windowHeight) 
    || (elBottom >= windowTop - windowHeight 
      && elBottom <= windowBottom);
}

function fetchNextPageIfNecessary() {
  if (isAlmostVisible($('main nav')) && nextPageURL) {
    fetchPageAndAppendArticles(nextPageURL);
  }
}

$(window).on('scroll', fetchNextPageIfNecessary);

If you don’t mind an extra dependency, you can use jquery.appear instead, which makes this a bit simpler:

$('main nav').on('appear', function () {
  if (nextPageURL) { 
    fetchPageAndAppendArticles(nextPageURL); 
  }
});

This brings us to the fun part: fetchPageAndAppendArticles. Since all my pages follow the same structure as my blog front page, this simply boils down to fetching the HTML of the page, parsing it with $.parseHTML, and then adding all the articles of the result to the end of the current page:

function fetchPageAndAppendArticles(url) {
  $.ajax(url).then(function (response) {
    $.each($.parseHTML(response), function (i, el) {
      var $main = $(el).find('main');
      if ($main.length) {
        // Append all the `article` elements.
        $main.find('article')
          .fadeIn(300)
          .insertAfter($('main article').last());

        // Get the URL for the next page to be fetched.
        nextPageURL = getNextPageURL($main);
        if (!nextPageURL) {
          // We loaded the last page. Hide the navbar.
          $('main nav').hide();
        }
      }
    });
  });
}

After the articles are loaded, update the URL pointing to the next page, and that’s it: the next time a visitor reaches the bottom of the page, the process will repeat itself!

You can do some extra finishing touches, such as replacing the navbar with a spinner icon and throttling the scroll event handler to avoid event overload. You can look at the full code here.

Published by

Remko Tronçon

Software Engineer · Hobby musician · BookWidgets