This is mainly for my own information retrieval, but perhaps it might be useful to another Javascript developer out there who is using ExtJS.

I’ve got a list of several thousand insurance plans I need to display to the users of one of my applications. I originally implemented a standard Ext.PagingToolbar to add to the footer of my data grid, but I’ve never been a fan of that navigation pattern, and I suspect that my users wouldn’t be either.

I then thought of sites like Google Reader that automatically retrieves the next set of records as soon as it detects that you’ve scrolled to a certain place in the reading element. A quick trip to my friend Google uncovered an article on The Code Project by Jason Witty that provided an example of just this technique.

A quick adaptation to my application provided a working example:

var emptyData;

// Set default scroll variables.
var currentpos = 0;
var currentpage = 1;

// Set constants.
//itemsperpage: number of records that appear on each page of content.
var itemsperpage = 100;

// Number of pages of content to fetch when offset reached.
var pagestofetch = 1;

// Number of pixel interval to fire handler request. You'll need to adjust this value
// depending on the location and size of your data grid
var scrolloffset = 1800;
var scrolloffsetinterval = 1800;

// Create the column model to display only the data needed for the users
var InsurerColumnModel = new Ext.grid.ColumnModel([{
       id:        'col1',
       header:    'Abbreviation',
       dataIndex: 'col1',
       width:     120
    },{
       id:        'col2',
       header:    'Name',
       dataIndex: 'col2',
       width:     240
    },{
       id:        'col3',
       header:    'Group',
       dataIndex: 'col3',
       width:     120
    }
]);

// Create a JSON Store to hold the data elements
var ScrollingProviderStore = new Ext.data.JsonStore({
	data:     emptyData,
	root:     'data',
	fields:   Insurer
});

// Create the grid to display each insurer
var InsurerGrid = new Ext.grid.GridPanel({
    store: ScrollingProviderStore,
    sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
    cm: InsurerColumnModel,
    renderTo: 'insurer-display-grid',
    stripeRows: true,
    width:'auto',
    height:350,
    autoScroll: true,
    autoExpandColumn:'col2',
    title:'Insurance Plans',
    frame:true
});

// Create an event listener for when the user scrolls the grid
InsurerGrid.addListener('bodyscroll',scrollListener);

// Fires when grid is scrolled
function scrollListener(scrollLeft, scrollTop){
    // Only handle scroll downs past highest position.
    if ( scrollTop > currentpos )
    {
        // Check if we should get more data
        if ( scrollTop >  scrolloffset )
        {
            // Store current grid scroll state.
            var state = InsurerGrid.getView().getScrollState();

            // Adjust scroll offset
            scrolloffset = scrollTop + scrolloffsetinterval;

            // Adjust current page
            currentpage = currentpage + 1;

            $.AjaxCFC({
	        url: 'ajaxCFC/ajax.cfc',
	        factory: 'application.framework',
	        bean: 'Insurance',
	        method: 'listProviders',
	        data: { 'start': currentpage },
	        timeout:30000,
	        useDefaultErrorHandler: false,
	        success: function(result) {
	            var insurers = eval('(' + result + ')');
	            ScrollingProviderStore.loadData(insurers,true);

                    // Restore the scrollbar state back to where the user
                    // triggered the load.
                    InsurerGrid.getView().restoreScroll(state);
	        },
                error: function(results) {
	            Ext.MessageBox.alert('Failed', 'Failed to retrieve insurers. Please try again.');
	        }
	   });
        }

        currentpos=scrollTop;
    }
};