First, you'll want to use jQuery for this
Second, put a placeholder on your page to contain your data.
<table id="dataTable" class="someClass" style="border-collapse: collapse;">
<colgroup>
<col width="12%" />
<col width="12%" />
<col width="12%" />
<!-- Define your column widths -->
</colgroup>
</table>
You'll need to code your own GetData method in a webservice, but this is the general idea (And call Refresh(); from your page load)
function Refresh() {
var getData = function(callback, context, startAt, batchSize) {
MyWebservice.GetData(
startAt, //What record to start at (1 to start)
batchSize, //Results per page
3, //Pages of data
function(result, context, method) {
callback(result, context, method);
},
null,
context
);
};
$('#dataTable').scrolltable(getData);
}
The getData function variable is passed into the scrolltable plugin, it will be called as needed when the table is being scrolled. The callback and context are passed in, and used by the plugin to manage the object you are operating on (context) and the asynchronous nature of the web (callback)
The GetData (note the case) webmethod needs to return a JSON object that contains some critical information, how your server side code does this is up to you, but the object this plugin expects is the following. The Prior and Post data are used to trigger when to load more data, basically, you can scroll through the middle/active page, but when you start seeing data in the prior or post page, we're going to need to fetch more data
return new {
// TotalRows in the ENTIRE result set (if it weren't paged/scrolled)
TotalRows = tableElement.Element("ResultCount").Value,
// The current position we are viewing at
Position = startAt,
// Number of items per "page"
PageSize = tableElement.Element("PageSize").Value,
// Number of pages we are working with (3)
PageCount = tableElement.Element("PageCount").Value,
// Data page prior to active results
PriorData = tbodyTop.Html(),
// Data to display as active results
CurrentData = tbodyCtr.Html(),
// Data to display after active results
PostData = tbodyBot.Html()
};
Next is the plugin itself
/// <reference path="../../js/jquery-1.2.6.js" />
(function($) {
$.fn.scrolltable = function(getDataFunction) {
var setData = function(result, context) {
var timeoutId = context.data('timeoutId');
if (timeoutId) {
clearTimeout(timeoutId);
context.data('timeoutId', null);
}
var $table = context.find("table");
var $topSpacer = $table.find('#topSpacer');
var $bottomSpacer = $table.find('#bottomSpacer');
var $newBodyT = $table.children('#bodyT');
var $newBodyC = $table.children('#bodyC');
var $newBodyB = $table.children('#bodyB');
var preScrollTop = context[0].scrollTop;
$newBodyT.html(result.PriorData);
$newBodyC.html(result.CurrentData);
$newBodyB.html(result.PostData);
var rowHeight = $newBodyC.children('tr').height() || 20;
var rowCountT = $newBodyT.children('tr').length;
var rowCountC = $newBodyC.children('tr').length;
var rowCountB = $newBodyB.children('tr').length;
result.Position = parseInt(result.Position);
$newBodyC.data('firstRow', result.Position);
$newBodyC.data('lastRow', (result.Position + rowCountC));
context.data('batchSize', result.PageSize);
context.data('totalRows', result.TotalRows);
var displayedRows = rowCountT + rowCountC + rowCountB;
var rowCountTopSpacer = Math.max(result.Position - rowCountT - 1, 0);
var rowCountBottomSpacer = result.TotalRows - displayedRows - rowCountTopSpacer;
if (rowCountTopSpacer == 0) {
$topSpacer.closest('tbody').hide();
} else {
$topSpacer.closest('tbody').show();
$topSpacer.height(Math.max(rowCountTopSpacer * rowHeight, 0));
}
if (rowCountBottomSpacer == 0) {
$bottomSpacer.closest('tbody').hide();
} else {
$bottomSpacer.closest('tbody').show();
$bottomSpacer.height(Math.max(rowCountBottomSpacer * rowHeight, 0));
}
context[0].scrollTop = preScrollTop; //Maintain Scroll Position as it sometimes was off
};
var onScroll = function(ev) {
var $scrollContainer = $(ev.target);
var $dataTable = $scrollContainer.find('#dataTable');
var $bodyT = $dataTable.children('tbody#bodyT');
var $bodyC = $dataTable.children('tbody#bodyC');
var $bodyB = $dataTable.children('tbody#bodyB');
var rowHeight = $bodyC.children('tr').height();
var currentRow = Math.floor($scrollContainer.scrollTop() / rowHeight);
var displayedRows = Math.floor($scrollContainer.height() / rowHeight);
var batchSize = $scrollContainer.data('batchSize');
var totalRows = $scrollContainer.data('totalRows');
var prevRowCount = $bodyT.children('tr').length;
var currRowCount = $bodyC.children('tr').length;
var postRowCount = $bodyB.children('tr').length;
var doGetData = (
(
(currentRow + displayedRows) < $bodyC.data('firstRow') //Scrolling up
&& (($bodyC.data('firstRow') - prevRowCount) > 1) // ...and data isn't already there
)
||
(
(currentRow > $bodyC.data('lastRow')) //Scrolling down
&& (($bodyC.data('firstRow') + currRowCount + postRowCount) < totalRows) // ...and data isn't already there
)
);
if (doGetData) {
var batchSize = $scrollContainer.data('batchSize');
var timeoutId = $scrollContainer.data('timeoutId');
if (timeoutId) {
clearTimeout(timeoutId);
$scrollContainer.data('timeoutId', null);
}
timeoutId = setTimeout(function() {
getDataFunction(setData, $scrollContainer, currentRow, batchSize);
}, 50);
$scrollContainer.data('timeoutId', timeoutId);
}
};
return this.each(function() {
var $dataTable = $(this);
if (!getDataFunction)
alert('GetDataFunction is Required');
var batchSize = batchSize || 25;
var outerContainerCss = outerContainerCss || {};
var defaultContainerCss = {
overflow: 'auto',
width: '100%',
height: '200px',
position: 'relative'
};
var containerCss = $.extend({}, defaultContainerCss, outerContainerCss);
if (! $dataTable.parent().hasClass('_outerContainer')) {
$dataTable
.wrap('<div class="_outerContainer" />')
.append($('<tbody class="spacer"><tr><td><div id="topSpacer" /></td></tr></tbody>'))
.append($('<tbody id="bodyT" />'))
.append($('<tbody id="bodyC" />'))
.append($('<tbody id="bodyB" />'))
.append($('<tbody class="spacer"><tr><td><div id="bottomSpacer" /></td></tr></tbody>'));
}
var $scrollContainer = $dataTable.parent();
$scrollContainer
.css(containerCss)
.scroll(onScroll);
getDataFunction(setData, $scrollContainer, 1, batchSize);
});
};
})(jQuery);
You'll likely need to tweak some things. I just converted it to a jQuery plugin and it's probably still a little glitchy.