lightweight infinite scroll with backbone.js
Solution 1
It's normal that myApp.routers.list.index()
doesn't work if there is the declaration of the Router, you need to call the instance of the router.
There are many things to say and I think the best explication is to see the code work and if It's that you want, learn the code.
I created an infinite listing with a "More" button to add models on the listing with using your code. The demo is on nodejitsu here : http://infinite-scroll.eu01.aws.af.cm/
You can show the complete code (client and server) on my gist on GitHub : https://gist.github.com/1522344 (I added a comment to explain how use the files)
Solution 2
Here's a lightweight implementation https://github.com/joneath/infiniScroll.js
Solution 3
Here is another solution http://backbonetutorials.com/infinite-scrolling/
Solution 4
I have created an extend of Backbone.Collection for easy using:
_.extend Backbone.Collection.prototype,
options:
infinitescroll:
success: $.noop
error: $.noop
bufferPx: 40
scrollPx: 150
page:
current: 0
per: null
state:
isDuringAjax: false
isDone: false
isInvalid: false
loading:
wrapper: 'backbone-infinitescroll-wrapper'
loadingId: 'backbone-infinitescroll-loading'
loadingImg: 'loading.gif'
loadingMsg: '<em>Loading ...</em>'
finishedMsg: '<em>No more</em>'
msg: null
speed: 'fast'
infinitescroll: (options={})->
# NOTE: coffeescript cannot deal with nested scope!
that = @
_.extend(@options.infinitescroll, options.infinitescroll) if options.infinitescroll
infinitescroll_options = @options.infinitescroll
# where we want to place the load message in?
infinitescroll_options.loading.wrapper = $(infinitescroll_options.loading.wrapper)
if !infinitescroll_options.loading.msg and infinitescroll_options.loading.wrapper.size() > 0
infinitescroll_options.loading.msg = $('<div/>', {
id: infinitescroll_options.loading.loadingId
}).html('<img alt="'+$(infinitescroll_options.loading.loadingMsg).text()+'" src="' + infinitescroll_options.loading.loadingImg + '" /><div>' + infinitescroll_options.loading.loadingMsg + '</div>')
infinitescroll_options.loading.msg.appendTo(infinitescroll_options.loading.wrapper).hide()
else
infinitescroll_options.loading.msg = null
fetch_options = _.omit(options, 'infinitescroll')
infinitescroll_fetch = ()=>
# mark the XHR request
infinitescroll_options.state.isDuringAjax = true
# increase page count
infinitescroll_options.page.current++
payload = {
page: infinitescroll_options.page.current
}
payload['limit'] = infinitescroll_options.page.per if infinitescroll_options.page.per isnt null
_.extend(fetch_options, {
remove: false
data: payload
})
if infinitescroll_options.loading.msg
# preload loading.loadingImg
(new Image()).src = infinitescroll_options.loading.loadingImg if infinitescroll_options.loading.loadingImg
infinitescroll_options.loading.msg.fadeIn infinitescroll_options.loading.speed, ()->
that.fetch(fetch_options)
.success (data, state, jqXHR)=>
infinitescroll_options.state.isDuringAjax = false
infinitescroll_options.state.isDone = true if _.size(data) is 0
infinitescroll_options.loading.msg.fadeOut infinitescroll_options.loading.speed, ()->
infinitescroll_options.success.call(data, state, jqXHR) if _.isFunction(infinitescroll_options.success)
return
return
.error (data, state, jqXHR)=>
infinitescroll_options.state.isDuringAjax = false
infinitescroll_options.state.isInvalid = true
infinitescroll_options.loading.msg.fadeOut infinitescroll_options.loading.speed, ()->
infinitescroll_options.error.call(data, state, jqXHR) if _.isFunction(infinitescroll_options.error)
return
return
return
else
that.fetch(fetch_options)
.success (data, state, jqXHR)=>
infinitescroll_options.state.isDuringAjax = false
infinitescroll_options.state.isDone = true if _.size(data) is 0
infinitescroll_options.success.call(data, state, jqXHR) if _.isFunction(infinitescroll_options.success)
return
.error (data, state, jqXHR)=>
infinitescroll_options.state.isDuringAjax = false
infinitescroll_options.state.isInvalid = true
infinitescroll_options.error.call(data, state, jqXHR) if _.isFunction(infinitescroll_options.error)
return
return
$(document).scroll ()->
$doc = $(document)
isNearBottom = ()->
bottomPx = 0 + $doc.height() - $doc.scrollTop() - $(window).height()
# if distance remaining in the scroll (including buffer) is less than expected?
(bottomPx - infinitescroll_options.bufferPx) < infinitescroll_options.scrollPx
return if infinitescroll_options.state.isDuringAjax || infinitescroll_options.state.isDone || infinitescroll_options.state.isInvalid || !isNearBottom()
infinitescroll_fetch()
return
infinitescroll_fetch()
return
You can see the implementation at https://gist.github.com/mcspring/7655861
pedalpete
Originally from Whistler, Canada, now living in Bondi Beach, Aus. I like building interesting things, algorithms, UX/UI, getting into hardware and RaspberryPi.
Updated on June 14, 2022Comments
-
pedalpete almost 2 years
i've looked at pagination in backbone https://gist.github.com/838460, and it all seems very heavy handed for what I'm looking for.
I want to do an infinite scroll type paging, and I'm new to backbone, so maybe I'm just not understaning it correctly.
what I thought I would do is get the first collection, click a 'next' button, and get the results and just append that to the original collection and render the newly added items.
So I have this in my Router I have an index function
if(!myApp.list){ myApp.list = new myApp.collections.list; myApp.list.page = 1; } else { myApp.list.page++; } myApp.list.url='/recipes?page='+myApp.list.page; myApp.list.fetch({ add: true, success: function() { new myApp.views.list({ collection: myApp.list}); }, error: function() { new Error({ message: "Error loading documents." }); } });
which will create the collection if it does't exist, and if it does exist, increment the 'page' before requesting the next items in the list.
so the first part of my question is, is there anything wrong with this way of doing things?? Seems much simpler than the other solutions I've seen.
Question #2 seems ridiculous, but how do I then trigger the 'next' button to get the next list??
In my view, I have a 'next' button, but calling myApp.routers.list.index or myApp.views.list doesn't give me an updated list.
-
pedalpete over 12 yearsthanks for going through all that work @Atinux, I think I have a much better understanding now. And from your response, I assume there is nothing wrong with doing it this way? It does seem much clearer than the other methods I've seen.
-
Atinux over 12 yearsI don't think there is something wrong this way. If there is a best practice to do this, please let me know. In my mind, the simplest way is the better, for the code and for the user.
-
Dave Stibrany almost 12 yearsThis plugin is great! Simple and well written.
-
Zach over 11 yearsJust so you know, the nodejitsu link is broken.
-
Brad Larson over 10 yearsAt some point, that gist is going to die. We prefer if you put the code directly into your answer, like I did here. That prevents this from going away when your gist does.
-
Upperstage almost 10 yearsLink to example is broken.