14

I'm toying around with backbone.js and I'm wondering if there is more concise solution for creating an 'endless-scroll' situation for models/collection than the modules I've been looking at (there are several for jquery, probably more for other libraries).

Some searching turned up next to nothing, so I thought I'd ask before getting into trying to build my own solution with backbone, if it something I should build outside a backbone collection, or if someone has attempted something similar.

blueblank
  • 4,064
  • 9
  • 41
  • 70

6 Answers6

10

This blog post landed on my twitter stream recently. Looks like a very nice solution and works well on the main page. From the blog the benefits of this solution are:

  1. Preserves the back button.
  2. Does NOT use the hashbang, no matter how happy twitter engineers are with it.
  3. Gives up on infinitely scrolling if (1) is impossible.
  4. Progressively enhances: when (3) occurs, the user follows a good ol' hyperlink for more content.
andyb
  • 42,062
  • 11
  • 113
  • 146
  • That is interesting, I'll look into it. – blueblank May 23 '11 at 23:48
  • 3
    I just wrote an infinite scroll tutorial also http://backbonetutorials.com/infinite-scrolling/ – Thomas Davis Apr 28 '12 at 04:18
  • The article gives very good points. However, personally I'd say don't freak out about non-js fallbacks http://developer.yahoo.com/blogs/ydn/many-users-javascript-disabled-14121.html – albertpeiro Apr 28 '13 at 10:15
  • @albertpeiro Agreed, although historically for the sites I have worked on it was a _requirement_ to support non-js for accessibility reasons and support for screen readers. I believe screen readers are much better at interpreting JavaScript and dynamic pages now. – andyb Apr 28 '13 at 12:37
4

I found two other resources while researching this myself:

  1. https://github.com/joneath/infiniScroll.js
  2. http://addyosmani.com/blog/backbone-paginator-new-pagination-components-for-backbone-js/
Jhony Fung
  • 2,796
  • 4
  • 25
  • 32
1

SlickGrid could be an option if you haven't tried it: https://github.com/mleibman/SlickGrid/wiki/Examples

34m0
  • 4,688
  • 1
  • 26
  • 22
0

Another solution with Backbone and jQuery waypoints: https://gist.github.com/davidmontoyago/6336612

davidmontoyago
  • 1,735
  • 13
  • 17
0

Following extension should work: https://github.com/joneath/infiniScroll.js

These may also be useful to load data partially: http://backplug.io/plugin/Backbone.actAs.Paginatable, https://github.com/wyuenho/backbone-pageable, https://github.com/backbone-paginator/backbone.paginator.

I prefer searching Backbone extensions here: http://backplug.io.

Vadym Myrgorod
  • 306
  • 3
  • 9
-1

Another repeated answer: Here is my extension of Backbone.Collection:

_.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

https://gist.github.com/mcspring/7655861

Brad Larson
  • 168,330
  • 45
  • 388
  • 563
Spring MC
  • 9
  • 2