Unspace

Home
Work
Projects

Discover
On the Road
Contact
Ruby on Rails Rails Pub Nite

Endless Pageless: No More Next Page

Bio_pete
:: Development
October 31, 2006 - over 3 years ago



Platform: We like Rails
Tested Browsers: Firefox 1.5, IE 6.0, Safari 2.0.4
Tag As: Pagination, Ajax, Rails, Patterns
Editor: Liz Clayton
Illustration: Anthony Watts
Source Code: here - version 1.02
Demo Page: Live Filter

Twelve years after the debut of search engines, we have Google representing the current best-of-breed index of web pages. It is faster, smarter, and it has raised the bar for web usability several times over. And yet, we are still paging through search results ten or twenty records at a time. Unfortunately, this style of navigation has been adopted by every site that returns records from a database, regardless of the amount of data being served.

We propose an alternative interface to result data on the web: the "Endless Pageless". Using Ajax we can dynamically fetch more results and append them to the bottom of the page, as the user approaches the end of the content they have already read. This pattern removes the need for pagination, feeding your customer as much data as they care to read in a subtle, non-intrusive way.

A balancing act

There are certainly pros to paging navigation systems that should not be discounted. They help break up a page, and provide spatial cues for moving around inside a set of results.

Yet, it is the wrong metaphor for many problem domains on the web. We are so quick to ape the "page" concept, as if the sophisticated web applications we are building resemble a paper book. This is superficial and ultimately it does damage to our user experience.

On a dynamic site, it is less useful to refer to content by page. "Page 4" only has context for right now; the item you just found might be on another page tomorrow - or not turn up at all. The book metaphor becomes misleading and self-defeating. A mandatory mouse click every page takes a user's attention off of the content they are looking at, forcing them to ponder which page they should be on. It's not an optimal, natural progression.

Leave paging to Google

Google has to present billions of potential matches in a way that serves 99% of people, 99% of the time — without leaving power users with specific needs out in the cold. Some would argue that this is a nice problem to have; however, if you're trying to provide access to a small database with 200 or 20,000 records, pagination is the wrong solution for presenting the information. Often the ease with which this content can be accessed determines whether a user makes a site part of their typical browsing experience — or not.

Much to the detriment of users, pagination is still implemented in a semi-unique way on every site. It's often easy for a developer to glaze over the user experience of such an important feature, but minor differences in how these buttons and links behave seriously impact the average end-user. It is therefore worth our while to get these presentation details right.

Implementation

The key to creating your own Pageless is to always know how far into the page a user has read. We create event handlers that trigger an Ajax request if the user comes within an adjustable distance from the bottom of the screen. Using the setTimeout function built into JavaScript, we can create a very responsive control loop that scans the current browser scrollTop attribute at tight intervals. By setting the onmousedown and onmouseup we prevent any sudden, unwelcome screen jerking.

As we move down the screen we save our current location to a temporary cookie, which allows us to resume our place on the page if we return to the results using the browser's Back button. The trick is to define an empty, invisible DIV element which is programatically moved down the page to your previous location at page load. It acts like a magical cursor, triggering all necessary content loading as it scrolls down the page. After a brief delay, the page state has been restored without breaking the Back button.

On the server we set up a function to respond to the Ajax calls for more pages of content. It will need to accept the required page as a parameter, and it should pass the corresponding rendered HTML snippet back to the client, at which point you will append it to the bottom of the content already rendered. Thankfully, implementation of the innerHTML attribute is consistent across all major browsers.

Most of the logic for making this work is performed on the client side. The good news for you is that all of the hard work has been done for you; go ahead and use search.js in your projects! We make use of the Prototype JavaScript library that is a part of Rails, but it could be modified to use another Ajax library, if necessary.

On the results page you must render values into several hidden INPUT elements: history_index, total_pages, and page_id. history_index will start off empty, and get modified by the client script. total_pages needs to be the total number of pages that the user's query could theoretically return at n records per page. Rails gives us this number automatically when we perform a paginated find call. Finally, history_index is just a unique number; we fill it with the number of milliseconds since 1970. There are also several DIVs, initially hidden, which you will modify to suit your application.

One caveat is that you need to make sure your server side function returns a signal to the client if no records are found. The way that the Ajax class in prototype works, you will need to receive at least one character for it to be properly handled.

We do stress that testing your Pageless in all major browsers is mandatory! It can be very tricky to get everything feeling "just right". Further, you might consider implementing Open Says Me on your project, and downgrading to a simple pagination system for those using older browsers.

Conclusion

The first really great Pageless implementation that we saw was Humanized Reader; their major contribution to the pattern was to store the current location a cookie, and return to it with the magic DIV cursor. We used some of their tricks in our example. (Thanks, Aza!)

Over the next several months, we expect to see many interpretations and innovations of these concepts as people get creative with the pattern. Several early attempts were coded to use an embedded IFRAME; while this was an interesting first step, the results felt awkward and left a lot to be desired. The main problem with an IFRAME is the potential for having two sets of scroll navigation on your page; it's very confusing to the user.

We're excited to see what people come up with next!

Postscript

We decided to do an experiment and include the Endless Pageless functionality in the demo for our Live Filter article.. and not tell anyone about it.

After over 16000 visits to the Live Filter article, only one person noticed that the search results didn't include pagination.

We did this to prove to ourselves that this was a viable user interface pattern that can be used with confidence.

Post-Postscript

I had the distinct pleasure of meeting Aza Raskin of Humanized at the Ajax Experience conference. Keep your eye on these folks, they are about to push the curve.

Pursudoscript

Recently, Unspace unveiled Pursudo. It's a simple, free site where you can meet interesting people and get off the internet.

You guessed it: no "Next" buttons here... so far, so good.

Demo Page: Live Filter

Source Code: here - version 1.02