canvas-datagrid
Version:
Canvas based data grid web component. Capable of displaying millions of contiguous hierarchical rows and columns without paging or loading, on a single canvas element.
131 lines (130 loc) • 5.79 kB
JavaScript
/*jslint browser: true*/
/*globals canvasDatagrid: false*/
function demo() {
'use strict';
var grid, dataCache, scrollDebounce, loadingText = 'Loading...';
function intializeCache() {
var x;
dataCache = [];
// the API does not return the number of records, but this is apparently the number
// it's important to set the length of the data correctly
// to allow the scroll box to be the correct height
// if not, you can't reach the last record
// Most APIs provide the total number of records in the set
// this API does not, so it is hard coded here
dataCache.length = 156800;
for (x = 0; x < dataCache.length; x += 1) {
// create empty "loading..." rows
dataCache[x] = {
id: x,
loaded: false,
category: loadingText,
question: loadingText,
answer: loadingText,
value: 0,
airdate: 0
};
}
// set the stub data to the grid. We'll mutate the data later as pages load
grid.data = dataCache;
// adjust the schema based on the key indexes
// defined above (id, loaded, category, etc..) so it looks nice
grid.schema[0].hidden = true;
grid.schema[1].hidden = true;
grid.schema[2].width = 150;
grid.schema[3].width = 400;
grid.schema[4].width = 250;
grid.schema[5].width = 75;
grid.schema[6].width = 100;
// set the type of the number and date columns for good form
// if this was a sortable table this would actually matter
grid.schema[5].type = 'number';
// we will use the date type to bind this column to a formatting function later
grid.schema[6].type = 'date';
}
function updateLocalCache(data, offset) {
var d, x;
// merge data from remote into local cache data
for (x = 0; x < data.length; x += 1) {
d = dataCache[x + offset];
d.loaded = true;
d.category = data[x].category.title;
d.question = data[x].question;
d.answer = data[x].answer;
d.value = data[x].value || 0;
d.airdate = data[x].airdate;
}
// the grid cannot detect when the data has changed
// so draw is called after data update
grid.draw();
}
function dataAdapter(offset, rows, callback) {
// THANKS TO http://jservice.io for providing this free service!!
var url, xhr = new XMLHttpRequest();
// bind an event to change the cursor to let the user know it's loading
xhr.addEventListener('progress', function () {
document.body.style.cursor = 'wait';
});
// onload, parse the object, change the cursor back and call the callback (updateLocalCache)
xhr.addEventListener('load', function () {
var data = JSON.parse(this.responseText);
document.body.style.cursor = 'auto';
callback(data, offset, rows);
});
// rows is not used by this API, other APIs usually define some sort of records per page
url = 'http://jservice.io/api/clues?offset=:offset'
.replace(':offset', offset)
.replace(':rows', rows);
xhr.open('GET', url);
xhr.send();
}
// instantiate the grid
grid = canvasDatagrid();
grid.attributes.globalRowResize = true;
grid.style.cellWhiteSpace = 'normal';
grid.style.cellHeight = 80;
grid.style.height = '100%';
grid.style.width = '100%';
// format dates so they are easy to read
// grid.formatters.<type> determine how data
// of a certain type is formatted as it is drawn
grid.formatters.date = function (e) {
if (!e.value) { return ''; }
var d = new Date(e.value);
return d.getMonth() + '/' + d.getDay() + '/' + d.getFullYear();
};
// API does not support filtering, so remove the option from the context menu;
grid.attributes.showFilter = false;
// stick the grid in the <div id="grid"> element defined in the HTML doc
document.getElementById('grid').appendChild(grid);
// warn people reordering does not work in this free sample API
// (but if it did, you could implement it here)
grid.addEventListener('click', function (e) {
if (e.cell.rowIndex === -1 && e.cell.columnIndex > -1) {
alert('The free jservice.io API does not support ordering or filtering');
e.preventDefault();
}
});
// attach a scroll event to the grid, will fire when the scroll box changes
grid.addEventListener('scroll', function () {
// ensure we don't overwhelm the API with requests from quickly scrolling
clearTimeout(scrollDebounce);
scrollDebounce = setTimeout(function () {
// check what part of the virtual canvas is visible by using grid.scrollIndexRect
// then check the records in that set to see if they need to be fetched or if they
// are already in the cache
if (grid.data.filter(function (d, i) {
return i > grid.scrollIndexRect.top && i < grid.scrollIndexRect.bottom && d.loaded === false;
}).length > 0) {
var offset = grid.scrollIndexRect.top,
rows = grid.scrollIndexRect.bottom - grid.scrollIndexRect.top;
dataAdapter(offset, rows, updateLocalCache);
}
}, 300);
});
// create rows of empty data the size of the data to later stick data into
intializeCache();
// grab the first page
dataAdapter(0, 100, updateLocalCache);
}
document.addEventListener('DOMContentLoaded', demo);