Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
zmillman committed Apr 12, 2014
1 parent dafdbad commit 3a3eeb9
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 2 deletions.
103 changes: 101 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,103 @@
jquery-infinite-pages
jQuery Infinite Pages
=====================

Simple infinite pages for Rails + jQuery + Kaminari
A custom jQuery plugin for adding infinite scrolling to paginated HTML views, designed
for integration with the Kaminari Rails plugin.

When the user scrolls to the point where the `rel="next"` link is within a set number
of pixels of the bottom of the screen, an async request to the next page is triggered.
The response should then update the displayed data and the pagination link.

Installation
------------

Just copy the `jquery.infinite-pages.js.coffee` file in your `app/assets/javascripts`
folder.

(It would be awesome to make this a gem, so consider that a feature request :smile:)

Plugin Usage
------------

```
# Setup plugin and define optional event callbacks
$('.infinite-table').infinitePages
debug: true
buffer: 200 # load new page when within 200px of nav link
loading: ->
# jQuery callback on the nav element
$(this).text("Loading...")
success: ->
# called after successful ajax call
error: ->
# called after failed ajax call
$(this).text("Trouble! Please drink some coconut water and try again")
# Force load of the next page
$('.infinite-table').infinitePages('next')
# Pause firing of events on scroll
$('.infinite-table').infinitePages('pause')
# Resume...
$('.infinite-table').infinitePages('resume')
```

Rails/Kaminari Example
----------------------

Example `lessons_controller.rb`:

```ruby
class LessonsController
def index
@lessons = current_user.subscribed_lessons.page(params[:page])
end
end
```

Example `index.html.erb`:

```erb
<div class="infinite-table">
<table>
<thead>
<tr>
<th>Lesson</th>
<th>Length</th>
</tr>
</thead>
<tbody>
<%= render :partial => 'lessons', :object => @lessons %>
</tbody>
</table>
<p class="pagination">
<%= link_to_next_page(@lessons, 'Next Page'))%>
</p>
</div>
```

Example `_lessons.html.erb`:

```erb
<% @lessons.each do |lesson| %>
<tr>
<td><%= lesson.name %></td>
<td><%= lesson.length.format %></td>
</tr>
<% end %>
```

Example `index.js.erb`:

```javascript
// Append new data
$("<%=j render(:partial => 'lessons', :object => @lessons) %>").appendTo($(".infinite-table tbody"));

// Update pagination link
<% if answers.last_page? %>
$('.pagination').remove();
<% else %>
$('.pagination').html("<%=j link_to_next_page(@lessons, 'Next Page'))%>");
<% end %>
```
118 changes: 118 additions & 0 deletions jquery.infinite-pages.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
###
jQuery Infinite Pages v0.1.0
https://github.com/magoosh/jquery-infinite-pages
Released under the MIT License
###

#
# Built with a class-based template for jQuery plugins in Coffeescript:
# https://gist.github.com/rjz/3610858
#

(($, window) ->
# Define the plugin class
class InfinitePages

# Default settings
defaults:
debug: false # set to true to log messages to the console
navSelector: 'a[rel=next]'
buffer: 1000 # 1000px buffer by default
loading: null # optional callback when next-page request begins
success: null # optional callback when next-page request finishes
error: null # optional callback when next-page request fails
state:
paused: false
loading: false

# Constructs the new InfinitePages object
#
# container - the element containing the infinite table and pagination links
constructor: (container, options) ->
@options = $.extend({}, @defaults, options)
@$container = $(container)
@$table = $(container).find('table')

@init()

# Setup and bind to related events
init: ->
$(window).scroll =>
@check()

This comment has been minimized.

Copy link
@yuki24

yuki24 Apr 13, 2014

Doing a lot of things in scroll event slows down the browser. jQuery creator John Resig suggests not to do this in this blog post. I think it's better to do something like the following:

checkInterval = 250
didScroll = false

$(window).scroll -> didScroll = true

setInterval (->
  if (didScroll)
    didScroll = false
    check()
), checkInterval

This comment has been minimized.

Copy link
@zmillman

zmillman Apr 13, 2014

Author Contributor

Thanks for the tip! I created an issue to improve this: #1

This comment has been minimized.

Copy link
@zmillman

zmillman Apr 14, 2014

Author Contributor

I just added debouncing to the event handler so that it will be triggered less frequently and handled asynchronously :)


# Internal helper for logging messages
_log: (msg) ->
console?.log(msg) if @options.debug

# Check the distance of the nav selector from the bottom of the window and fire
# load event if close enough
check: ->
nav = @$container.find(@options.navSelector)
if nav.size() == 0
@_log "No more pages to load"
else
windowBottom = $(window).scrollTop() + $(window).height()
distance = nav.offset().top - windowBottom

if @options.state.paused
@_log "Paused"
else if @options.state.loading
@_log "Waiting..."
else if (distance > @options.buffer)
@_log "#{distance - @options.buffer}px remaining..."
else
@next() # load the next page

# Load the next page
next: ->
if @options.state.done
@_log "Loaded all pages"
else
@_loading()

$.getScript(@$container.find(@options.navSelector).attr('href'))
.done (=> @_success())
.fail (=> @_error())

_loading: ->
@options.state.loading = true
@_log "Loading next page..."
if typeof @options.loading is 'function'
@$container.find(@options.navSelector).each(@options.loading)

_success: ->
@options.state.loading = false
@_log "New page loaded!"
if typeof @options.success is 'function'
@$container.find(@options.navSelector).each(@options.success)

_error: ->
@options.state.loading = false
@_log "Error loading new page :("
if typeof @options.error is 'function'
@$container.find(@options.navSelector).each(@options.error)

# Pause firing of events on scroll
pause: ->
@options.state.paused = true
@_log("Scroll checks paused")

# Resume firing of events on scroll
resume: ->
@options.state.paused = false
@_log("Scroll checks resumed")
@check()

# Define the plugin
$.fn.extend infinitePages: (option, args...) ->
@each ->
$this = $(this)
data = $this.data('infinitepages')

if !data
$this.data 'infinitepages', (data = new InfinitePages(this, option))
if typeof option == 'string'
data[option].apply(data, args)

) window.jQuery, window

0 comments on commit 3a3eeb9

Please sign in to comment.