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");
     }
    }
  });
}
 
29
Kudos
 
29
Kudos

Now read this

tail -f | grep -v “foo” | grep -v “bar” | grep -v “baz”

That looks pretty standard, right? Most of the time CLI one liners are good. Sometimes they are slow or we are just down right doing something wrong. Our syslog at work moves so fast on our Development Vagrant Instances and you need more... Continue →