Endless / Infinite Pagination In Dropdown
Recently I have been re-implementing the Notification Menu at OneHealth in Backbone + Marionette and wanted to incorporate endless pagination, real-time dynamic updates, and just an overall clean / simple user experience.
When building the endless pagination I had noticed when you scroll to the end of the list the body starts to scroll. Well, That’s annoying. Okay, so lets throw in a e.preventDefault()
on the list scroll binding. Hmm, that doesn’t prevent the current list from scrolling. Now What?
Lets start with some simple View Setup
//Module Variables Used
this.eol
(Was The End Of List Hit)
this.lastFire
(Height of Last Pagination Fire)
this.collection
(Backbone Collection)
...
ui : {
container : '.js-notification-list-container',
list : '.js-notification-list',
loadingBar : '.js-loading-bar',
endOfList : '.js-endOfList'
},
/**
* Method For Paginating through Collection
*/
loadNext : function(){
var self = this;
this.ui.loadingBar.show();
this.collection
.nextResults(10)
.done(function() {
self.ui.loadingBar.hide();
})
.fail(function() {
self.endOfList = true;
self.ui.loadingBar.hide();
self.ui.endOfList.show();
});
},
onRender : function() {
this.attachScrollWatcher();
}
….
Here’s what I did to achieve the desired result
When attaching a scroll listener to the view;
I attached a listener for mousewheel DOMMouseScroll
events on the DOM.
Then I prevent the document from scrolling when scrolling within the current list. A throttle is added so the scroll feels smoother. Then if the list is scrolled to the (bottom height- buffer
) and the current list height has not attempted to paginate then the collection paginate method is fired and the lastFire
is updated to the current List Height.
….
/**
* View Event Handler Called On View Render
*/
attachScrollWatcher : function() {
var self = this;
var lastFire = 0;
//buffer so list fires before end
var buffer = 100;
this.ui.container.on('mousewheel DOMMouseScroll', function (e) {
var y = $(this).scrollTop(),
h = $(this).get(0).scrollHeight,
ah = $(this).height();
//-----------------
// Scroll Prevention
//-----------------
var oe = e.originalEvent,
delta = oe.wheelDelta || -oe.detail,
scrollTo = false,
throttle = 0.2;
if (e.type === 'mousewheel') {
scrollTo = delta * -1 * throttle;
} else if (e.type === 'DOMMouseScroll') {
scrollTo = delta;
}
if (scrollTo) {
e.preventDefault();
$(this).scrollTop(scrollTo + $(this).scrollTop());
}
//-----------------
if ((y + ah) >= (h-buffer) && self.lastFire !== h && !self.eol) {
//Store last fired Height so we won't try again
lastFire = h;
if (self.collection.length >= 10) {
self.loadNext();
} else {
console.log("Too Few Items For Pagination");
}
}
});
}