#114
Jun 16, 2008

Endless Page

Ever wondered how some sites seem to have endless scrolling page with no pagination? Find out how easy it is to do this in Rails in this episode.
Tags: ajax
Download (17.7 MB, 8:43)
alternative download for iPod & Apple TV (11.1 MB, 8:43)

Alternative Solutions

# products_controller.rb
def index
  @products = Product.paginate(:page => params[:page], :per_page => 15)
end

# index.js.rjs
page.insert_html :bottom, :products, :partial => @products
if @products.total_pages > @products.current_page
  page.call 'checkScroll'
else
  page[:loading].hide
end

# application_helper.rb
def javascript(*args)
  content_for(:head) { javascript_include_tag(*args) }
end
<!-- index.html.erb -->
<% title "Products" %>
<% javascript :defaults, 'endless_page' %>

<div id="products">
  <%= render :partial => @products %>
</div>
<p id="loading">Loading more page results...</p>
// endless_page.js
var currentPage = 1;

function checkScroll() {
  if (nearBottomOfPage()) {
    currentPage++;
    new Ajax.Request('/products.js?page=' + currentPage, {asynchronous:true, evalScripts:true, method:'get'});
  } else {
    setTimeout("checkScroll()", 250);
  }
}

function nearBottomOfPage() {
  return scrollDistanceFromBottom() < 150;
}

function scrollDistanceFromBottom(argument) {
  return pageHeight() - (window.pageYOffset + self.innerHeight);
}

function pageHeight() {
  return Math.max(document.body.scrollHeight, document.body.offsetHeight);
}

document.observe('dom:loaded', checkScroll);

RSS Feed for Episode Comments 40 comments

1. peanut Jun 16, 2008 at 00:48

Nice! Thanks!


2. Ismail Mohd Noor Jun 16, 2008 at 03:18

Hi Ryan,

I am always been a great fan of your railscast. Keep up the good work.


3. Sam Nardoni Jun 16, 2008 at 03:39

Nice implementation. I'll definitly be using this in the near future. Thanks


4. Victor Brylew Jun 16, 2008 at 04:33

Hi Ryan.

Small question and answers.

What are advantages of the already created endless page plugin?

1)You don't need to install will_paginate or other page based long list implementation
2)You can configure your settings for each endless page independently. For example, you can set offset of 300px from bottom for one page and 100px for other if you want
3)You will have all your configuration in one place instead of set of 'magic integers' inside JS part of application
4)Workable in the IE, Opera and FF.

@all: we are living on the open source world. If you see something you don't like in the existing variant then just let me know and we can improve/fix it.


5. Ryan Bates Jun 16, 2008 at 08:25

@Victor, good points. However, I usually avoid adding plugin dependencies unless it greatly simplifies my code. Here writing the implementation from scratch is nearly as simple as using a plugin. This way I understand the code better, and it's easier for me to customize to my specific needs.

That said, sometimes it's better to not reinvent the wheel. The fact that your plugin works out of the box in IE and Opera is a good example of the power behind using an existing, well tested implementation.

I encourage any developer to look at all the options out there and find the best one that fits the requirements for their application. Here I just give another option.


6. keli Jun 16, 2008 at 11:20

beautiful Ryan even tho I'm not gonna use this feature in my pages right now..but I know where to find it..

thanks again..


7. Mike Wyatt Jun 16, 2008 at 14:11

hey ryan, sounds like you've got a cold. if that's the case, feel better soon

that's a brilliant little implementation


8. Jason Deppen Jun 16, 2008 at 20:44

I love the idea behind this but I'm concerned with SEO. The only way I can think about getting around it is to still have the pagination links but use css to hide them. Then the bots can follow the links to index the pages.

Any thoughts?


9. Ryan Bates Jun 16, 2008 at 21:34

@Mike, yep, got a cold. Thanks. :)

@Jason, a similar concern is graceful degradation. If someone has javascript disabled they should still be able to access the next page. Keeping the pagination links might be a good idea, or at least the "next page" link.


10. Adam Hill Jun 17, 2008 at 04:04

Hi Ryan,

Sometimes knowing which pages are loaded are important from a statistcal pov. Using non-ajax pagination is my preference in those cases largely due to typically using client-side JS site stat tracking like Google Analytics.

Although it's relatively trivial to track statistics for action & param based server-side tracking eg /products?page=2 etc, does someone know if there is anyway to track these AJAX pagination calls using Google Analytics? Like a ping back or a function call in the .rjs response?


11. Ryan Bates Jun 17, 2008 at 07:31

@Adam, doesn't Google Analytics provide some javascript to place on your site? Maybe you could send that javascript back in the RJS call. I haven't looked into it, but you'll probably need to add the URL to that JS to point to the requested page.


12. Kevin Whinnery Jun 17, 2008 at 11:32

Hi Ryan,

What do you use to do the syntax coloring for the code samples you post in these examples? Does the Syntax gem do that?


13. Kevin Whinnery Jun 17, 2008 at 11:35

Ah, CodeRay....

View source always helps, sorry bout that... :S


14. Andrew Greenberg Jun 17, 2008 at 19:14

Its a good idea to focus on why auto-pagination is a good idea from a UI perspective. The default of numbered page links, while convenient for coding, imposes an interface using numbers that have no real meaning in terms of the data being reviewed. Page 4 is just an ordinal index to the data being presented. Rarely will you know how to find what you are looking for by using ordinal pagination of this type, particularly for VERY long lists, and for more modest lists, its simply an annoying interference with linear browsing through the table.

Auto paging interfaces, I think pioneered by Aza Raskin in his Humanized Reader, provides a more natural, more humane approach to browsing data. The list, if too long for sequential browsing may require additional effort, but ordinal pagination is not the right vehicle for that. Sorting filters of various kinds, or an index that has some semantic relationship with the data being searched/browsed is the way to go.

The only question is how to get it done. I'm astonished at how easy it was to do this.

Next step is to look at Raskin's admonition against using warning messages when what we really mean is to provide an undo function. Make that easy, and we have a truly killer framework.

Great presentation, Ryan. I hope this approach ris widely adopted, for it is truly A GOOD IDEA.


15. Geoff Jacobsen Jun 18, 2008 at 00:32

The window position code does indeed not work in IE6.

Here is some (hopefully) browser agnostic code to calculate how close to the bottom one is (using pageHeight() from the example):

var pos = pageHeight()-(document.viewport.getScrollOffsets()[1]+document.viewport.getHeight())

pos is the number pixels the bottom of the page is from being visible. A Negative number means your page is shorter the your browser window.


16. Adam Hill Jun 18, 2008 at 06:57

Just a follow up on the tracking of the statistics of the ajax pagination and hence more accurately see your site usage... I found an article at http://www.google.com/support/analytics/bin/answer.py?hl=en-nz&answer=55519 that explains how to do it if anyone has the same concerns.


17. MIslav Jun 18, 2008 at 07:34

Nice episode, Ryan. But your voice *does* sound strange :)

Someone wants this functionality on Twitter.com? Check out http://userscripts.org/scripts/show/24398


18. Guest Jun 21, 2008 at 22:19

Just... a big up to Ryan for all his very quality AND FREE screencasts ! Not an error in my file or other... just thank you ! ;-)

An Happy RailsCasts Watcher.


19. stefek Jul 02, 2008 at 01:40

those stories are getting better and better


20. ratze Jul 03, 2008 at 00:50

Nice!
JQuery:
http://github.com/jney/jquery.pageless/tree/master/jquery.pageless.js


21. Eric Jul 06, 2008 at 14:33

Looks like there is an error in the endless.js file

TypeError: Value undefined (result of expression document.observe) is not object.

Line 24:
document.observe('dom:loaded', checkScroll());

What could be wrong?

Thnx.


22. Eric Jul 06, 2008 at 14:50

Sorry, previous post is not correct, this is what Firebug tells me:

document.observe is not a function
endless_page.js?1215380276()()

[document.observe('dom:loaded', checkScroll);


23. Shane Jul 21, 2008 at 22:44

I ran into a little gotcha, well at least it was a gotcha for a noob like myself.

Results were not as intended, checked dev server and noticed a "406 Not Acceptable" error. A little research on Google provided: "406 Not Acceptable is for when the server cannot return the response body in any of the formats specified in the request's Accept header."

I then guessed that I needed to update my controller (built from scaffolding) to accept rjs and included a: format.js { render :rjs => @products }

Reload, refresh, and viola! Hope this helps any other Ruby & Rails noobies such as myself.

 


24. David Ngo Jul 24, 2008 at 10:11

Excellent screencast Ryan


25. Christian Jul 31, 2008 at 04:03

hi ryan,
sweetness once again...
but unfortunately it's not working in the IE because of the window.pageYOffset and the innerHeight. both returns undefined in IE. that's why i need to implement a browser switch in scrollDistanceFromBottom(). anyway. nice.
bless chris


26. Christian Aug 01, 2008 at 04:42

my second impression is, that an endless list is nice, but it hides what we are scrolling. does the list contains bockwursts or plums or whatever... i mean the list header gets lost by scrolling.
an iframe thingie could be a solution?

best regards
chris


27. Matt Aug 02, 2008 at 05:14

How would this work with caching?


28. Nakort Valles Oct 02, 2008 at 17:50

Hi, i am implementing this for my applicatino, and seem to be running into some problems because my page is shorter then my browser window and i am getting some negative numbers, which activate the checkScroll function when it doesnt have to be. Anybody run into this same issue? Any ideas on how to solve this? Thanks in advance.


29. Air Jordan Shoes Nov 02, 2008 at 23:26

so good, thanks for your good job, Ryan!


30. cheap wow gold Nov 05, 2008 at 01:17

it's really a good news for me, i get the secret now.


31. tiffany Jewelry Nov 18, 2008 at 21:39

Excellent screencast Ryan


32. tiffany Jewelry Nov 18, 2008 at 21:39

Good article, thank you!


33. Sword of the New World money Nov 20, 2008 at 17:57

Thanks Ryan,I think this is one of the most wonderful sites. I have great admiration for you.


34. Sword of the New World Vis Nov 20, 2008 at 17:59

Thanks Ryan,I think this is one of the most wonderful sites. I have great admiration for you.


35. cheap holic gold Nov 20, 2008 at 18:33

Thanks Ryan,I think this is one of the most wonderful sites. I have great admiration for you.


36. kamas Nov 21, 2008 at 21:31

I have great admiration for you.


37. lastchaos gold Nov 24, 2008 at 23:00

I have great admiration for you.


38. lily Nov 27, 2008 at 19:03

Thank you Ryan, your screencast is good. Please look at our URL, if necessary we can learn from each other.


39. lily Nov 27, 2008 at 19:35

Thank you Ryan, your screencast is good. Please look at our URL, if necessary we can learn from each other.


40. evden eve nakliyat Dec 01, 2008 at 01:25

evden eve nakliyat

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player