Creating an endless scrolling list
Endless scrolling lists were popularized by social networking websites, such as Facebook and Twitter. Their goal is to create the illusion that the entire available content has already been loaded. Additionally, with this technique, interruptions to the normal scrolling that are caused by the user trying to find the button for the next page are avoided.
At the same time, we would also want to avoid unnecessary waste of bandwidth; this means that loading the whole set of data at once is not an option.
The solution is to monitor the user's scrolling and detect the approach at the bottom of the page. When the user is sufficiently close to the bottom, we can automatically load the next page of content by appending it to the end of the currently shown content.
Getting ready
You must already have a service that provides the content on a page-by-page basis. This example works without such a service by default, but to make it fully functional, an actual HTTP server is needed in order for the Ajax requests for the next page to work.
How to do it...
Let's write the HTML page, CSS style, and JavaScript code.
Create a file named
index.html
that will contain the full HTML, CSS, and JavaScript code of our example. We need to insert a DOCTYPE into our HTML document; otherwise, the browser will operate in "quirks mode" and the height measurement function$(window).height()
will not work.<!DOCTYPE HTML>
We'll add a content placeholder element in the page:
<div id="content"></div>
For demonstration purposes, we'll add the following CSS code to make the pages visible. Feel free to skip this CSS:
div.page { min-height: 1200px; width: 800px; background-color:#f1f1f1; margin:0.3em; font-size: 3em; } div.error { color:#f00; }
Finally, we add the JavaScript code. First we load jQuery:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"> </script>
Then we can add our script:
<script type="text/javascript"> (function() {
Our page getter calls the callback with a null error argument and a simple string containing the page number as the content (for example,
Page 1
), but it can also perform an Ajax request. See the following code for more info on how to modify it to make an Ajax request.This function is artificially limited to 10 pages of content. After the tenth page, the callback function is called with an error, indicating that there are no more pages available:
var page = 1; function getPage(callback) { if (page <= 10) callback(null, 'Page ' + page); else callback("No more pages"); page += 1; };
We use
triggerPxFromBottom
to specify when to start loading the next page. When onlytriggerPxFromBottom
pixels remain to be scrolled, the loading of the next page will begin. Its value is set to0
; this means that the user must reach the end of the currently visible page to trigger the loading process:var currentlyLoading = false; var triggerPxFromBottom = 0;
loadNext
appends the next page into the#content
div. However, if the callback function is called with an error, it will displayNo more content
below the last part of the page. After an error event, no more pages will be loaded. This means that whengetPage
returns an error, our code will stop loading new pages. This is the desired behavior:function loadNext() { currentlyLoading = true; getPage(function(err, html) { if (err) { $("<div />") .addClass('error') .html("No more content") .appendTo("#content"); } else { $("<div />") .addClass('page') .html(html).appendTo("#content"); currentlyLoading = false; } }); }
This event handler is called when the page is scrolled in any way. It calculates the number of pixels of scrolling that remain below. If the number of the pixels is small enough and the code is not currently loading a page, it calls the page-loading function:
$(window).on('scroll', function() { var remainingPx = $(document).height() - $(window).scrollTop() - $(window).height(); if (remainingPx <= triggerPxFromBottom && !currentlyLoading) loadNext(); });
Finally, we call
loadNext()
for the first time to load the first page:loadNext(); }()); </script>
How it works...
The visible area of the browser (also called the viewport) has its own dimensions that can be fetched by calling jQuery's $.fn.height()
function on the $(window)
object. On the other hand, $(document).height()
provides us with the height of the entire content of the page. Finally, $(window).scrollTop()
gives us the scroll offset.
Using these functions, we can calculate the remaining pixels to be scrolled. Then we recalculate and check this value every time the user scrolls the page. If the value is sufficiently small, we call our loading function. At the same time, we make sure to stop loading new pages until the current loading process is finished. (Otherwise, the user's scrolling actions might load a couple of more pages while they wait for the content to load.)
There's more...
Here is a possible Ajax implementation of the getPage
function. This function sends Ajax requests to a request handler hosted on the same domain at the path /pages/<number>
to retrieve the HTML contents of the next page:
function getPage(cb) { $.get('/pages/' + page) .success(function(html) { cb(null, html); }) .error(function() { cb("Error"); } page += 1; }
To make this version work, you will need to implement the request handler in your server-side code.
Your server-side code can return an error, such as 404, to indicate that there is no more content available. As a result, jQuery will never call our success callback, and our code will stop loading new pages.
The endless scrolling list recipe provides great user experience, but it has one significant drawback. We must make sure that we don't have any important page content below the contents
element. This means that page elements placed at the bottom (usually the footer links and copyright messages) might become unreachable.