Convert any pager (views, search, ...) to an autoloading endless scroll autopager with just a few lines of code.

Step 1:

Download the file jquery.infinitescroll.min.js from https://github.com/paulirish/infinite-scroll and put it in /sites/all/themes/YOURTHEME/js/jquery.infinitescroll.min.js.

If you are using responsive design with flexible images (and thus no widht/height in the html), it is recommended to also add the imagesloaded plugin to be sure your images have a width and height before they get added. Add the file jquery.imagesloaded.min.js from https://github.com/desandro/imagesloaded. If you don't install this plugin you need to remove the $container.imagesLoaded( function(){ wrapper from the examples below.

Step 2:

Add the js file to the info file of your theme

Step 3:

Make a custom javascript file /sites/all/themes/YOURTHEME/js/YOURTHEME.js with contents (Drupal 6).

Drupal 6 code

/**
 * Implementation of autopager @see https://github.com/paulirish/infinite-scroll
 * All views that have the "autopager" class will have an autopager
 */
Drupal.behaviors.viewsInfiniteScroll = function(context) {
  $(function(){

    var $container = $('div.autopager div.view-content');

    $container.imagesLoaded( function(){
      $container.infinitescroll({
        navSelector  : 'div.autopager .pager',    // selector for the paged navigation
        nextSelector : 'div.autopager .pager-next a',  // selector for the NEXT link (to page 2)
        itemSelector : 'div.autopager .views-row',     // selector for all items you'll retrieve
        loading: {
          finishedMsg: 'No more pages to load.',
          img: Drupal.settings.basePath + 'sites/all/themes/YOURTHEME/images/loading.gif'
        }
      })
    })
  });
}

Drupal 7 code

(function ($) {

  Drupal.behaviors.viewsInfiniteScroll = {
    attach: function (context, settings) {
      $(function(){

        var $container = $('div.autopager div.view-content');

        $container.imagesLoaded( function(){
          $container.infinitescroll({
            navSelector  : 'div.autopager .pager',    // selector for the paged navigation
            nextSelector : 'div.autopager .pager-next a',  // selector for the NEXT link (to page 2)
            itemSelector : 'div.autopager .views-row',     // selector for all items you'll retrieve
            loading: {
              finishedMsg: 'No more pages to load.',
              img: Drupal.settings.basePath + 'sites/all/themes/YOURTHEME/images/loading.gif'
            }
        })
    })
  });
  }
};

})(jQuery);

The above example converts the pager of every view that has the class "autopager" (which you can set in the views interface) to an auto load more pager.

You can easily adapt the css classes to target the pager of a list of search results or any other pager. Just make sure the classes are specific enough so you target only one pager in case there are blocks that use views or others.

The default loading image can be found at http://www.infinite-scroll.com/loading.gif, but you can use any loading image.

More settings

In the "All options" section of http://www.infinite-scroll.com/ you can find more information how to change loading text and other options.

Adapt the javascript code if you don't want an automatic loading but a load more button: http://www.infinite-scroll.com/trigger.html

Example code

$('#body').infinitescroll({
      navSelector  : "a#next:last",            
                     // selector for the paged navigation (it will be hidden)
      nextSelector : "a#next:last",   
                     // selector for the NEXT link (to page 2)
      itemSelector : "#body p"          
                     // selector for all items you'll retrieve
    });
 
    // kill scroll binding
    $(window).unbind('.infscr');
    
    // hook up the manual click guy.
    $('a#next').click(function(){
      $(document).trigger('retrieve.infscr');
      return false;
    });
    
    // remove the paginator when we're done.
    $(document).ajaxError(function(e,xhr,opt){
      if (xhr.status == 404) $('a#next').remove();
    });

STEP 4: Add CSS of infinite scroll loader

#infscr-loading {
  position: fixed;
  text-align: center;
  bottom: 300px;
  left: 42%;
  z-index: 100;
  background: white;
  background: hsla( 0, 0%, 100%, 0.9 );
  padding: 20px;
  color: #222;
  font-size: 15px;
  font-weight: bold;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
}

STEP 5: Make sure jquery version is newer than 1.7.1.

Use one of the methods described in http://drupal.org/node/1058168 for installing a newer version of jquery in Drupal.

Update - 02-02-2012 for D7

Great info, but as said in http://drupal.org/node/1472304#comment-5819046
You need to hack the js lib downloaded (https://github.com/paulirish/infinite-scroll) to support drupal syntax by replacing :
the \/ to & in all occurrence matching
"match(/^(.*?page=)2(\/.*|$)/" => "match(/^(.*?page=)2(&.*|$)/"

Comments

ydahi’s picture

I'm running into a bit of a snag:

If I set a value for items per page in the views pager, I get the loading animation, however it returns with "No More Pages to load" even if there are.

If I set a value of 0 for the items per page, I don't get any animation at all.

Something I missed?

ahanchi’s picture

i'm trying to make the infinity scroll script works with a more link button but without success may anyone help me please..this is my script:
(function ($) {

Drupal.behaviors.viewsInfiniteScroll = {
attach: function (context, settings) {
$(function(){

var container = $(".view-content");

container.infinitescroll({
navSelector : "ul.pager",
// selector for the paged navigation (it will be hidden)
nextSelector : "a#next",
// selector for the NEXT link (to page 2)
itemSelector : ".views-row .node-page",
// selector for all items you'll retrieve

});

// kill scroll binding
$(window).unbind('.infscr');

// hook up the manual click guy.
$('a#next').click(function(){
//alert("eeee");
$(document).trigger('retrieve.infscr');
return false;
});

// remove the paginator when we're done.
$(document).ajaxError(function(e,xhr,opt){
if (xhr.status == 404) $('a#next').remove();
});

});
}
};

})(jQuery);

ydahi’s picture

I found a solution here: http://drupal.org/node/1808018

Uses jMasonry with InfiniteScroll. Works like a charm.

mohit_aghera’s picture

Hi All,

What should i change in the Drupal 7 code given below to make it work with search result page.
I have html structure is given like this . I don't know where should i change to work with search result page..

(function ($) {

  Drupal.behaviors.viewsInfiniteScroll = {
    attach: function (context, settings) {
      $(function(){

        var $container = $('div.autopager div.view-content');

        $container.imagesLoaded( function(){
          $container.infinitescroll({
            navSelector  : 'div.autopager .pager',    // selector for the paged navigation
            nextSelector : 'div.autopager .pager-next a',  // selector for the NEXT link (to page 2)
            itemSelector : 'div.autopager .views-row',     // selector for all items you'll retrieve
            loading: {
              finishedMsg: 'No more pages to load.',
              img: Drupal.settings.basePath + 'sites/all/themes/YOURTHEME/images/loading.gif'
            }
        })
    })
  });
  }
};

})(jQuery);

-Mohit

Johann Wagner’s picture

I couldn't get this to work on a simple pager, built calling the pager theme.

By the way, the official demo of infinite scroll's jquery plugin doesn't work either.

mohit_aghera’s picture

Hi Johann Wagner,
I have make it work..
Here is code for my infinitescroll.js

(function($){
	Drupal.behaviors.viewsInfiniteScroll = {
		    attach: function (context, settings) {
		      $(function(){
		    	if (this.processed) return;
		    	this.processed=true;
		    	if ($('.search-results').length==0) return;
		        var $container = $('.search-results');
		        $container.imagesLoaded( function(){
		          $container.infinitescroll({
		            navSelector  : 'ul.pager',    // selector for the paged navigation
		            nextSelector : '.pager-next a',  // selector for the NEXT link (to page 2)
		            itemSelector : '.search-results',     // selector for all items you'll retrieve
		            animate      : true,
		            msgText  : Drupal.t("Loading new results..."),
		            img: '/sites/default/themes/egrievance/images/ajax-loader.gif',
		            donetext:Drupal.t('No more results to load.'),
		        },function(arrayOfNewElems,state){
		        	//YOUR CALLBACK STUFF .. To re-attach behaviour if needed
		        	
		          })
		    })
		  });
		  }
		};
})(jQuery);

Remember here i wanted to do it on search result so i added .search-results in $container.

You can download all the files from here https://gist.github.com/mohit-rocks/5660610

With the help of these files and procedure shown above i achieved the infinite scroll.

-Mohit

Sebbon’s picture

Hi, I can concurr this works as Mohit said. At first I used the body class of the search page (.page-search) but in his example it is more specific to the container of the retrieved results on the search page; the container selector is the immediate wrapper of the search results (. search-results)

Works like a charm! Thanks!

arvigeus’s picture

For some reason, the information on site is outdated. If you want to load more content on click:
https://github.com/paulirish/infinite-scroll/issues/204#issuecomment-747...

three2em’s picture

So I read, and reread, and then pulled my hair out over this entry. When I finally got down and dirty with the infinite-scroll code, I realized that it was assuming that the first page had an index of 1 (which in Drupal it doesn't,) and didn't seem to be calculating the last page very well. So here is my implementation that works for Drupal default pager (not Views):

Drupal.behaviors.mysiteInfiniteScroll = {
    attach: function (context, settings) {
      $('div.item-list .pager:not(.mysite-scroll-processed)', context).each(function(){
		if($('#zone-content-wrapper div.item-list .pager:not(.mysite-scroll-processed) .pager-next').length == 0) return;
		
               // Determine the Last Page
		var $max_page = $('div.item-list li.pager-last > a').attr('href').substring($('div.item-list li.pager-last > a').attr('href').lastIndexOf("page=")+5);
               // Determine the Current Page
		var $curr_page = $('div.item-list li.pager-next > a').attr('href').substring($('div.item-list li.pager-next > a').attr('href').lastIndexOf("page=")+5) - 1;
		
                $('#zone-content-wrapper #block-system-main div.block-inner > div.content').infinitescroll({
			navSelector  : 'div.item-list ul.pager',    // selector for the paged navigation
                        nextSelector : 'div.item-list li.pager-next > a',  // selector for the NEXT link (to page 2)
                        itemSelector : '#block-system-main .node',     // selector for all items you'll retrieve
                        loading: {
                                finishedMsg: 'No more pages to load.',
                                img: Drupal.settings.basePath + 'sites/all/themes/mysite/Images/temp_loader.gif'
                        },
			bufferPx     : 650, // my own default pixel buffer
			donetext     : Drupal.t('No more results to load.'),
			maxPage      : $max_page, // calculated above
			state: {
				currPage : $curr_page // calculated above
			},
                   },function(arrayOfNewElems){
		        Drupal.attachBehaviors($(this));	
		});
		
		$(this).addClass('mysite-scroll-processed');
  	});
  }
};

This could probably use some cleaning up, and would greatly take suggestions. Also, I'm using jQuery 1.8 (using jQuery Update module). Hope this help any of you struggling to make this work outside of Views.

madhusudanmca’s picture

Hi,

I tried to integrate it with SOLR search results which has similar HTML structure to default search, but unfortunately this code doesn't give accurate results as it always appends the same data.

For e.g.: If at the time of page load our next page is "page=2" and last page is 5, in this case it simply appends 2 times the results of "page=2" to current results.

I have rewrite the complete code using jquery autopager "jQuery.autopager".

To integrate it with search, just download the jquery autopager and use below code:

(function($){
  var search_infinite_scroll_was_initialised = false;
  Drupal.behaviors.search_infinite_scroll = {
    attach:function() {
      // Make sure that autopager plugin is loaded
      if($.autopager) {
        if(!search_infinite_scroll_was_initialised) {
          search_infinite_scroll_was_initialised = true;
          if($('div.item-list ul.pager')) {
              var content_selector = '.search-results';
              var items_selector   = '.search-results';
              var pager_selector   = 'div.item-list ul.pager';
              var next_selector    = 'div.item-list li.pager-next > a';
              var img_path         = Drupal.settings.basePath + 'themes/bartik/images/ajax-loader.gif';
              var img              = '<div id="search_infinite_scroll-ajax-loader"><img src="' + img_path + '" alt="loading..."/></div>';
              $(pager_selector).hide();
              var handle = $.autopager({
                appendTo: content_selector,
                content: items_selector,
                link: next_selector,
                page: 0,
                start: function() {
                  $('.search-results').after(img);
                },
                load: function() {
                  $('div#search_infinite_scroll-ajax-loader').remove();
                  Drupal.attachBehaviors(this);
                }
              });

              // Trigger autoload if content height is less than doc height already
              var prev_content_height = $(content_selector).height();
              do {
                var last = $(items_selector).filter(':last');
                if(last.offset().top + last.height() < $(document).scrollTop() + $(window).height()) {
                  last = $(items_selector).filter(':last');
                  handle.autopager('load');
                }
                else {
                  break;
                }
              }
              while ($(content_selector).height() > prev_content_height);

          }
        }
      }
      else {
        alert(Drupal.t('Autopager jquery plugin in not loaded.'));
      }
    }
  }
})(jQuery);
JordanMagnuson’s picture

This seems to sort of be working for me, but only seems to autopage once (i.e. I get double the results, but that's it... no more autopaging when I hit the bottom of the page...)

Any ideas?

madhusudanmca’s picture

Are you getting any JS or some other errors??
If so please can you explain.

babruix’s picture

For custom coded page I have created own version for replacing drupal pager with "read more" AJAX functionality: (works without infinite-scroll plugin)

(function ($) {
  Drupal.behaviors.readMoreAjax = {
    attach: function (context, settings) {
      $('.read-more-ajax', context).click(function () {
        var nextPage = $('.pager .pager-next a').attr('href');
        var lastPage = $('.pager .pager-last a').attr('href');
        $.get(nextPage, function (data) {
          $(data).find('.produktlist').insertBefore($('.item-list'));
          $('.item-list .pager').remove();
          if (nextPage == lastPage) {
            $('.read-more-ajax').remove();
          }
          else {
            $(data).find('.item-list .pager').appendTo($('.item-list'));
            Drupal.attachBehaviors($('.item-list'));
          }
        });
      });
      $('.item-list .pager').hide();
    }
  };
})(jQuery);

Where:

  • '.read-more-ajax' - button to load more
  • '.produktlist' - nodes/divs that should be loaded
vincenzocacciatore’s picture

Hi,
I have tried the minimal set of options to make the infinite scroll working in Drupal 7. It looks working fine, I have only to pages at the moment, but the last page keeps being loaded. I saw that the pager (which is hidden) is not updated and so it always has the "next" page link.
Any hint?

Thanks,
Vince

voja_’s picture

Many thanks to @babruix. Below is amended code for search results page:

(function ($) {
  Drupal.behaviors.loadMoreAjax = {
    attach: function (context, settings) {
      $('.load-more-ajax', context).click(function () {
        var nextPage = $('.pager .pager-next a').attr('href');
        var lastPage = $('.pager .pager-last a').attr('href');
        $.get(nextPage, function (data) {
          $(data).find('.search-results.node-results').insertBefore($('.item-list'));
          $('.item-list .pager').remove();
          if (nextPage == lastPage) {
            $('.load-more-ajax').remove();
          }
          else {
            $(data).find('.item-list .pager').appendTo($('.item-list'));
            Drupal.attachBehaviors($('.item-list'));
          }
        });
      });
      $('.item-list .pager').hide();
    }
  };
})(jQuery);

Remember to add the button (class="load-more-ajax") in the search results.tpl.php.

steveOR’s picture

Yes, thanks @babruix and @voha_ this is a great working solution using the default Drupal pager and just a small js behavior function with no additional libraries needed. Was trying to solve this via custom ajax to the backend and got stuck when it didn't seem that module_invokes into search module was going to allow me add the page number argument.

zeev’s picture

Thanks, for the snippet.

schtink’s picture

The Views Pager starts at 0, not 1. So when the infinite-scroll.js lib looks for a page 2 it gets a 404.

Add this to your defaults and voila!

state: {currPage: 0}

Here's my entire code snippet, including support for masonry if anyone is interested:

(function($){
	Drupal.behaviors.viewsInfiniteScroll = {
		    attach: function (context, settings) {
		      $(function(){
		    	if (this.processed) return;
		    	this.processed=true;
		    	if ($('.blist-inside').length==0) return;
		        var $container = $('.blist-inside');
		
				// setup masonry
			  	$container.imagesLoaded(function($){
			    	$container.masonry({
			      		itemSelector: 'div.pmason',
			      		columnWidth: 320
			    	});
			  		}(jQuery));
		
		        $container.imagesLoaded( function(){
		          $container.infinitescroll({
		            navSelector  : 'ul.pager',    // selector for the paged navigation
		            nextSelector : '.pager-next a',  // selector for the NEXT link (to page 2)
		            itemSelector : 'div.pmason',     // selector for all items you'll retrieve
		            animate      : true,
		            msgText  : Drupal.t("Loading new results..."),
		            img: '/sites/default/themes/sq/img/loading.gif',
		            donetext:Drupal.t('No more results to load.'),
		
					state: {
					            currPage: 0
					        },
				// call back for masonry
		        },function(arrayOfNewElems,state){
		        	// hide new items while they are loading
			      var $newElems = $( arrayOfNewElems ).css({ opacity: 0 });
			      // ensure that images load before adding to masonry layout
			      $newElems.imagesLoaded(function(){
			        // show elems now they're ready
			        $newElems.animate({ opacity: 1 });
			        $container.masonry( 'appended', $newElems, true ); 
				    });
		          })
		    })
		  });
		  }
		};
})(jQuery);
joncjordan’s picture

In Drupal 6, I just changed the Pager element to "1" which got it working for me.

fras42’s picture

In order to get it working don't forget Add views row classes have checked (in Views format settings),
since the itemSelector in the example uses .views-row class

di3gopa’s picture

For anyone that comes to this page, there is a module that already does this:

https://www.drupal.org/project/gd_infinite_scroll

The js used in this tutorial is not longer mantained.

wranvaud’s picture

Many years later and still useful on a custom page not using views or search! Somehow I missed this module but it's working now, thank you!

bibliophileaxe’s picture