Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

SlickGrid v2.1 - Implement virtual rendering for columns.

  • Loading branch information...
commit e4f25cbb63722475618f019b92d26aa935a0e1ad 1 parent 5b42d6b
@mleibman authored
View
6 examples/example-spreadsheet.html
@@ -23,6 +23,7 @@
<div class="options-panel">
<h2>Demonstrates:</h2>
<ul>
+ <li>Virtual scrolling on both rows and columns.</li>
<li>Select a range of cells with a mouse</li>
<li>Use Ctrl-C and Ctrl-V keyboard shortcuts to cut and paste cells</li>
<li>Use Esc to cancel a copy and paste operation</li>
@@ -66,10 +67,11 @@
}
];
- for (var i = 0; i < 26; i++) {
+ for (var i = 0; i < 100; i++) {
columns.push({
id: i,
- name: String.fromCharCode("A".charCodeAt(0) + i),
+ name: String.fromCharCode("A".charCodeAt(0) + (i / 26) | 0) +
+ String.fromCharCode("A".charCodeAt(0) + (i % 26)),
field: i,
width: 60,
editor: FormulaEditor
View
1  slick.grid.css
@@ -12,7 +12,6 @@ classes should alter those!
}
.slick-header-columns, .slick-headerrow-columns {
- width: 999999px;
position: relative;
white-space: nowrap;
cursor: default;
View
423 slick.grid.js
@@ -7,7 +7,7 @@
* Distributed under MIT license.
* All rights reserved.
*
- * SlickGrid v2.0
+ * SlickGrid v2.1
*
* NOTES:
* Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods.
@@ -68,7 +68,7 @@ if (typeof Slick === "undefined") {
asyncEditorLoadDelay: 100,
forceFitColumns: false,
enableAsyncPostRender: false,
- asyncPostRenderDelay: 60,
+ asyncPostRenderDelay: 50,
autoHeight: false,
editorLock: Slick.GlobalEditorLock,
showHeaderRow: false,
@@ -105,7 +105,7 @@ if (typeof Slick === "undefined") {
var page = 0; // current page
var offset = 0; // current page offset
- var scrollDir = 1;
+ var vScrollDir = 1;
// private
var initialized = false;
@@ -143,7 +143,9 @@ if (typeof Slick === "undefined") {
var prevScrollTop = 0;
var scrollTop = 0;
var lastRenderedScrollTop = 0;
+ var lastRenderedScrollLeft = 0;
var prevScrollLeft = 0;
+ var scrollLeft = 0;
var selectionModel;
var selectedRows = [];
@@ -153,6 +155,8 @@ if (typeof Slick === "undefined") {
var columnsById = {};
var sortColumns = [];
+ var columnPosLeft = [];
+ var columnPosRight = [];
// async call handles
@@ -184,6 +188,12 @@ if (typeof Slick === "undefined") {
options = $.extend({}, defaults, options);
columnDefaults.width = options.defaultColumnWidth;
+ columnsById = {};
+ for (var i = 0; i < columns.length; i++) {
+ var m = columns[i] = $.extend({}, columnDefaults, columns[i]);
+ columnsById[m.id] = i;
+ }
+
// validate loaded JavaScript modules against requested options
if (options.enableColumnReorder && !$.fn.sortable) {
throw new Error("SlickGrid's 'enableColumnReorder = true' option requires jquery-ui.sortable module to be loaded");
@@ -209,7 +219,8 @@ if (typeof Slick === "undefined") {
$focusSink = $("<div tabIndex='0' hideFocus style='position:fixed;width:0;height:0;top:0;left:0;outline:0;'></div>").appendTo($container);
$headerScroller = $("<div class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container);
- $headers = $("<div class='slick-header-columns' style='width:10000px; left:-1000px' />").appendTo($headerScroller);
+ $headers = $("<div class='slick-header-columns' style='left:-1000px' />").appendTo($headerScroller);
+ $headers.width(getHeadersWidth());
$headerRowScroller = $("<div class='slick-headerrow ui-state-default' style='overflow:hidden;position:relative;' />").appendTo($container);
$headerRow = $("<div class='slick-headerrow-columns' />").appendTo($headerRowScroller);
@@ -260,6 +271,7 @@ if (typeof Slick === "undefined") {
});
}
+ updateColumnCaches();
createColumnHeaders();
setupColumnSort();
createCssRules();
@@ -341,12 +353,22 @@ if (typeof Slick === "undefined") {
return dim;
}
+ function getHeadersWidth() {
+ var headersWidth = 0;
+ for (var i = 0, ii = columns.length; i < ii; i++) {
+ var width = columns[i].width;
+ headersWidth += width;
+ }
+ headersWidth += scrollbarDimensions.width;
+ return Math.max(headersWidth, viewportW) + 1000;
+ }
+
function getCanvasWidth() {
var availableWidth = viewportHasVScroll ? viewportW - scrollbarDimensions.width : viewportW;
var rowWidth = 0;
var i = columns.length;
while (i--) {
- rowWidth += (columns[i].width || columnDefaults.width);
+ rowWidth += columns[i].width;
}
return options.fullWidthRows ? Math.max(rowWidth, availableWidth) : rowWidth;
}
@@ -358,6 +380,7 @@ if (typeof Slick === "undefined") {
if (canvasWidth != oldCanvasWidth) {
$canvas.width(canvasWidth);
$headerRow.width(canvasWidth);
+ $headers.width(getHeadersWidth());
viewportHasHScroll = (canvasWidth > viewportW - scrollbarDimensions.width);
}
@@ -472,12 +495,11 @@ if (typeof Slick === "undefined") {
});
$headers.empty();
+ $headers.width(getHeadersWidth());
$headerRow.empty();
- columnsById = {};
for (var i = 0; i < columns.length; i++) {
- var m = columns[i] = $.extend({}, columnDefaults, columns[i]);
- columnsById[m.id] = i;
+ var m = columns[i];
var header = $("<div class='ui-state-default slick-header-column' id='" + uid + m.id + "' />")
.html("<span class='slick-column-name'>" + m.name + "</span>")
@@ -1021,6 +1043,8 @@ if (typeof Slick === "undefined") {
h.width(columns[i].width - headerColumnWidthDiff);
}
}
+
+ updateColumnCaches();
}
function applyColumnWidths() {
@@ -1093,8 +1117,29 @@ if (typeof Slick === "undefined") {
return columns;
}
+ function updateColumnCaches() {
+ // Pre-calculate cell boundaries.
+ columnPosLeft = [];
+ columnPosRight = [];
+ var x = 0;
+ for (var i = 0, ii = columns.length; i < ii; i++) {
+ columnPosLeft[i] = x;
+ columnPosRight[i] = x + columns[i].width;
+ x += columns[i].width;
+ }
+ }
+
function setColumns(columnDefinitions) {
columns = columnDefinitions;
+
+ columnsById = {};
+ for (var i = 0; i < columns.length; i++) {
+ var m = columns[i] = $.extend({}, columnDefaults, columns[i]);
+ columnsById[m.id] = i;
+ }
+
+ updateColumnCaches();
+
if (initialized) {
invalidateAllRows();
createColumnHeaders();
@@ -1195,12 +1240,12 @@ if (typeof Slick === "undefined") {
if (offset != oldOffset) {
var range = getVisibleRange(newScrollTop);
- cleanupRows(range.top, range.bottom);
+ cleanupRows(range);
updateRowPositions();
}
if (prevScrollTop != newScrollTop) {
- scrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1;
+ vScrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1;
$viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop);
trigger(self.onViewportChanged, {});
@@ -1252,7 +1297,8 @@ if (typeof Slick === "undefined") {
return item[columnDef.field];
}
- function appendRowHtml(stringArray, row) {
+ function appendRowHtml(stringArray, row, range) {
+ var cacheEntry = rowsCache[row];
var d = getDataItem(row);
var dataLoading = row < getDataLength() && !d;
var cellCss;
@@ -1269,39 +1315,66 @@ if (typeof Slick === "undefined") {
stringArray.push("<div class='ui-widget-content " + rowCss + "' style='top:" + (options.rowHeight * row - offset) + "px'>");
var colspan, m;
- for (var i = 0, cols = columns.length; i < cols; i++) {
+ for (var i = 0, ii = columns.length; i < ii; i++) {
m = columns[i];
- colspan = getColspan(row, i);
- cellCss = "slick-cell l" + i + " r" + Math.min(columns.length - 1, i + colspan - 1) + (m.cssClass ? " " + m.cssClass : "");
- if (row === activeRow && i === activeCell) {
- cellCss += (" active");
+ colspan = 1;
+ if (metadata && metadata.columns) {
+ var columnData = metadata.columns[m.id] || metadata.columns[i];
+ colspan = (columnData && columnData.colspan) || 1;
+ if (colspan === "*") {
+ colspan = ii - i;
+ }
}
- // TODO: merge them together in the setter
- for (var key in cellCssClasses) {
- if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) {
- cellCss += (" " + cellCssClasses[key][row][m.id]);
+ // Do not render cells outside of the viewport.
+ if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) {
+ if (columnPosLeft[i] > range.rightPx) {
+ // All columns to the right are outside the range.
+ break;
}
- }
- stringArray.push("<div class='" + cellCss + "'>");
+ appendCellHtml(stringArray, row, i, colspan);
+ }
- // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
- if (d) {
- var value = getDataItemValueForColumn(d, m);
- stringArray.push(getFormatter(row, m)(row, i, value, m, d));
+ if (colspan > 1) {
+ i += (colspan - 1);
}
+ }
- stringArray.push("</div>");
+ stringArray.push("</div>");
+ }
- if (colspan) {
- i += (colspan - 1);
+ function appendCellHtml(stringArray, row, cell, colspan) {
+ var m = columns[cell];
+ var d = getDataItem(row);
+ var cellCss = "slick-cell l" + cell + " r" + Math.min(columns.length - 1, cell + colspan - 1) +
+ (m.cssClass ? " " + m.cssClass : "");
+ if (row === activeRow && cell === activeCell) {
+ cellCss += (" active");
+ }
+
+ // TODO: merge them together in the setter
+ for (var key in cellCssClasses) {
+ if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) {
+ cellCss += (" " + cellCssClasses[key][row][m.id]);
}
}
+ stringArray.push("<div class='" + cellCss + "'>");
+
+ // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)
+ if (d) {
+ var value = getDataItemValueForColumn(d, m);
+ stringArray.push(getFormatter(row, m)(row, cell, value, m, d));
+ }
+
stringArray.push("</div>");
+
+ rowsCache[row].cellRenderQueue.push(cell);
+ rowsCache[row].cellColSpans[cell] = colspan;
}
+
function cleanupRows(rangeToKeep) {
for (var i in rowsCache) {
if (((i = parseInt(i, 10)) !== activeRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) {
@@ -1342,7 +1415,7 @@ if (typeof Slick === "undefined") {
if (!rows || !rows.length) {
return;
}
- scrollDir = 0;
+ vScrollDir = 0;
for (i = 0, rl = rows.length; i < rl; i++) {
if (currentEditor && activeRow === rows[i]) {
makeActiveCellNormal();
@@ -1487,26 +1560,31 @@ if (typeof Slick === "undefined") {
updateCanvasWidth(false);
}
- function getVisibleRange(viewportTop) {
+ function getVisibleRange(viewportTop, viewportLeft) {
if (viewportTop == null) {
viewportTop = scrollTop;
}
+ if (viewportLeft == null) {
+ viewportLeft = scrollLeft;
+ }
return {
top: Math.floor((viewportTop + offset) / options.rowHeight),
- bottom: Math.ceil((viewportTop + offset + viewportH) / options.rowHeight)
+ bottom: Math.ceil((viewportTop + offset + viewportH) / options.rowHeight),
+ leftPx: viewportLeft,
+ rightPx: viewportLeft + viewportW
};
}
- function getRenderedRange(viewportTop) {
- var range = getVisibleRange(viewportTop);
+ function getRenderedRange(viewportTop, viewportLeft) {
+ var range = getVisibleRange(viewportTop, viewportLeft);
var buffer = Math.round(viewportH / options.rowHeight);
var minBuffer = 3;
- if (scrollDir == -1) {
+ if (vScrollDir == -1) {
range.top -= buffer;
range.bottom += minBuffer;
- } else if (scrollDir == 1) {
+ } else if (vScrollDir == 1) {
range.top -= minBuffer;
range.bottom += buffer;
} else {
@@ -1517,25 +1595,146 @@ if (typeof Slick === "undefined") {
range.top = Math.max(0, range.top);
range.bottom = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, range.bottom);
+ range.leftPx -= viewportW;
+ range.rightPx += viewportW;
+
+ range.leftPx = Math.max(0, range.leftPx);
+ range.rightPx = Math.min(canvasWidth, range.rightPx);
+
return range;
}
function ensureCellNodesInRowsCache(row) {
var cacheEntry = rowsCache[row];
if (cacheEntry) {
- if (!cacheEntry.cellNodes) {
- cacheEntry.cellNodes = [];
- cacheEntry.cellNodesByColumnIdx = [];
-
- var columnIdx = 0, cellNodes = cacheEntry.rowNode.childNodes;
- for (var j = 0, jj = cellNodes.length; j < jj; j++) {
- cacheEntry.cellNodesByColumnIdx[columnIdx] = cacheEntry.cellNodes[j] = cellNodes[j];
- columnIdx += getColspan(row, columnIdx);
+ if (cacheEntry.cellRenderQueue.length) {
+ var lastChild = cacheEntry.rowNode.lastChild;
+ while (cacheEntry.cellRenderQueue.length) {
+ var columnIdx = cacheEntry.cellRenderQueue.pop();
+ cacheEntry.cellNodesByColumnIdx[columnIdx] = lastChild;
+ lastChild = lastChild.previousSibling;
}
}
}
}
+ function cleanUpCells(range, row) {
+ var totalCellsRemoved = 0;
+ var cacheEntry = rowsCache[row];
+
+ // Remove cells outside the range.
+ var cellsToRemove = [];
+ for (var i in cacheEntry.cellNodesByColumnIdx) {
+ // I really hate it when people mess with Array.prototype.
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(i)) {
+ continue;
+ }
+
+ // This is a string, so it needs to be cast back to a number.
+ i = i | 0;
+
+ var colspan = cacheEntry.cellColSpans[i];
+ if (columnPosLeft[i] > range.rightPx ||
+ columnPosRight[Math.min(columns.length - 1, i + colspan - 1)] < range.leftPx) {
+ if (!(row == activeRow && i == activeCell)) {
+ cellsToRemove.push(i);
+ }
+ }
+ }
+
+ var cellToRemove;
+ while ((cellToRemove = cellsToRemove.pop()) != null) {
+ cacheEntry.rowNode.removeChild(cacheEntry.cellNodesByColumnIdx[cellToRemove]);
+ delete cacheEntry.cellColSpans[cellToRemove];
+ delete cacheEntry.cellNodesByColumnIdx[cellToRemove];
+ if (postProcessedRows[row]) {
+ delete postProcessedRows[row][cellToRemove];
+ }
+ totalCellsRemoved++;
+ }
+ }
+
+ function cleanUpAndRenderCells(range) {
+ var cacheEntry;
+ var stringArray = [];
+ var processedRows = [];
+ var cellsAdded;
+ var totalCellsAdded = 0;
+ var colspan;
+
+ for (var row = range.top; row <= range.bottom; row++) {
+ cacheEntry = rowsCache[row];
+ if (!cacheEntry) {
+ continue;
+ }
+
+ // cellRenderQueue populated in renderRows() needs to be cleared first
+ ensureCellNodesInRowsCache(row);
+
+ cleanUpCells(range, row);
+
+ // Render missing cells.
+ cellsAdded = 0;
+
+ var metadata = data.getItemMetadata && data.getItemMetadata(row);
+ metadata = metadata && metadata.columns;
+
+ // TODO: shorten this loop (index? heuristics? binary search?)
+ for (var i = 0, ii = columns.length; i < ii; i++) {
+ // Cells to the right are outside the range.
+ if (columnPosLeft[i] > range.rightPx) {
+ break;
+ }
+
+ // Already rendered.
+ if ((colspan = cacheEntry.cellColSpans[i]) != null) {
+ i += (colspan > 1 ? colspan - 1 : 0);
+ continue;
+ }
+
+ colspan = 1;
+ if (metadata) {
+ var columnData = metadata[columns[i].id] || metadata[i];
+ colspan = (columnData && columnData.colspan) || 1;
+ if (colspan === "*") {
+ colspan = ii - i;
+ }
+ }
+
+ if (columnPosRight[Math.min(ii - 1, i + colspan - 1)] > range.leftPx) {
+ appendCellHtml(stringArray, row, i, colspan);
+ cellsAdded++;
+ }
+
+ i += (colspan > 1 ? colspan - 1 : 0);
+ }
+
+ if (cellsAdded) {
+ totalCellsAdded += cellsAdded;
+ processedRows.push(row);
+ }
+ }
+
+ if (!stringArray.length) {
+ return;
+ }
+
+ var x = document.createElement("div");
+ x.innerHTML = stringArray.join("");
+
+ var processedRow;
+ var node;
+ while ((processedRow = processedRows.pop()) != null) {
+ cacheEntry = rowsCache[processedRow];
+ var columnIdx;
+ while ((columnIdx = cacheEntry.cellRenderQueue.pop()) != null) {
+ node = x.lastChild;
+ cacheEntry.rowNode.appendChild(node);
+ cacheEntry.cellNodesByColumnIdx[columnIdx] = node;
+ }
+ }
+ }
+
function renderRows(range) {
var parentNode = $canvas[0],
stringArray = [],
@@ -1548,7 +1747,26 @@ if (typeof Slick === "undefined") {
}
renderedRows++;
rows.push(i);
- appendRowHtml(stringArray, i);
+
+ // Create an entry right away so that appendRowHtml() can
+ // start populatating it.
+ rowsCache[i] = {
+ "rowNode": null,
+
+ // ColSpans of rendered cells (by column idx).
+ // Can also be used for checking whether a cell has been rendered.
+ "cellColSpans": [],
+
+ // Cell nodes (by column idx). Lazy-populated by ensureCellNodesInRowsCache().
+ "cellNodesByColumnIdx": [],
+
+ // Column indices of cell nodes that have been rendered, but not yet indexed in
+ // cellNodesByColumnIdx. These are in the same order as cell nodes added at the
+ // end of the row.
+ "cellRenderQueue": []
+ };
+
+ appendRowHtml(stringArray, i, range);
if (activeCellNode && activeRow === i) {
needToReselectCell = true;
}
@@ -1560,12 +1778,8 @@ if (typeof Slick === "undefined") {
var x = document.createElement("div");
x.innerHTML = stringArray.join("");
- for (var i = 0, ii = x.childNodes.length; i < ii; i++) {
- rowsCache[rows[i]] = {
- "rowNode": parentNode.appendChild(x.firstChild),
- "cellNodes": null,
- "cellNodesByColumnIdx": null
- };
+ for (var i = 0, ii = rows.length; i < ii; i++) {
+ rowsCache[rows[i]].rowNode = parentNode.appendChild(x.firstChild);
}
if (needToReselectCell) {
@@ -1602,7 +1816,12 @@ if (typeof Slick === "undefined") {
// remove rows no longer in the viewport
cleanupRows(rendered);
- // add new rows
+ // add new rows & missing cells in existing rows
+ if (lastRenderedScrollLeft != scrollLeft) {
+ cleanUpAndRenderCells(rendered);
+ }
+
+ // render missing rows
renderRows(rendered);
postProcessFromRow = visible.top;
@@ -1610,27 +1829,29 @@ if (typeof Slick === "undefined") {
startPostProcessing();
lastRenderedScrollTop = scrollTop;
+ lastRenderedScrollLeft = scrollLeft;
h_render = null;
}
function handleScroll() {
scrollTop = $viewport[0].scrollTop;
- var scrollLeft = $viewport[0].scrollLeft;
- var scrollDist = Math.abs(scrollTop - prevScrollTop);
+ scrollLeft = $viewport[0].scrollLeft;
+ var vScrollDist = Math.abs(scrollTop - prevScrollTop);
+ var hScrollDist = Math.abs(scrollLeft - prevScrollLeft);
- if (scrollLeft !== prevScrollLeft) {
+ if (hScrollDist) {
prevScrollLeft = scrollLeft;
$headerScroller[0].scrollLeft = scrollLeft;
$topPanelScroller[0].scrollLeft = scrollLeft;
$headerRowScroller[0].scrollLeft = scrollLeft;
}
- if (scrollDist) {
- scrollDir = prevScrollTop < scrollTop ? 1 : -1;
+ if (vScrollDist) {
+ vScrollDir = prevScrollTop < scrollTop ? 1 : -1;
prevScrollTop = scrollTop;
// switch virtual pages if needed
- if (scrollDist < viewportH) {
+ if (vScrollDist < viewportH) {
scrollTo(scrollTop + offset);
} else {
var oldOffset = offset;
@@ -1640,20 +1861,24 @@ if (typeof Slick === "undefined") {
invalidateAllRows();
}
}
+ }
+ if (hScrollDist || vScrollDist) {
if (h_render) {
clearTimeout(h_render);
}
- if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH) {
- if (Math.abs(lastRenderedScrollTop - scrollTop) > 20) {
+ if (Math.abs(lastRenderedScrollTop - scrollTop) > 20 ||
+ Math.abs(lastRenderedScrollLeft - scrollLeft) > 20) {
+ if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH &&
+ Math.abs(lastRenderedScrollLeft - scrollLeft) < viewportW) {
render();
+ } else {
+ h_render = setTimeout(render, 50);
}
- } else {
- h_render = setTimeout(render, 50);
- }
- trigger(self.onViewportChanged, {});
+ trigger(self.onViewportChanged, {});
+ }
}
trigger(self.onScroll, {scrollLeft: scrollLeft, scrollTop: scrollTop});
@@ -1661,22 +1886,34 @@ if (typeof Slick === "undefined") {
function asyncPostProcessRows() {
while (postProcessFromRow <= postProcessToRow) {
- var row = (scrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--;
+ var row = (vScrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--;
var cacheEntry = rowsCache[row];
- if (!cacheEntry || postProcessedRows[row] || row >= getDataLength()) {
+ if (!cacheEntry || row >= getDataLength()) {
continue;
}
+ if (!postProcessedRows[row]) {
+ postProcessedRows[row] = {};
+ }
+
ensureCellNodesInRowsCache(row);
- for (var i = 0; i < cacheEntry.cellNodesByColumnIdx.length; i++) {
- var m = columns[i];
- if (m.asyncPostRender) {
- var node = cacheEntry.cellNodesByColumnIdx[i];
- m.asyncPostRender(node, postProcessFromRow, getDataItem(row), m);
+ for (var columnIdx in cacheEntry.cellNodesByColumnIdx) {
+ if (!cacheEntry.cellNodesByColumnIdx.hasOwnProperty(columnIdx)) {
+ continue;
+ }
+
+ columnIdx = columnIdx | 0;
+
+ var m = columns[columnIdx];
+ if (m.asyncPostRender && !postProcessedRows[row][columnIdx]) {
+ var node = cacheEntry.cellNodesByColumnIdx[columnIdx];
+ if (node) {
+ m.asyncPostRender(node, postProcessFromRow, getDataItem(row), m);
+ }
+ postProcessedRows[row][columnIdx] = true;
}
}
- postProcessedRows[row] = true;
h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay);
return;
}
@@ -2051,18 +2288,26 @@ if (typeof Slick === "undefined") {
$focusSink[0].focus();
}
- function scrollActiveCellIntoView() {
- if (activeCellNode) {
- var left = $(activeCellNode).position().left,
- right = left + $(activeCellNode).outerWidth(),
- scrollLeft = $viewport.scrollLeft(),
- scrollRight = scrollLeft + $viewport.width();
+ function scrollCellIntoView(row, cell) {
+ var colspan = getColspan(row, cell);
+ var left = columnPosLeft[cell],
+ right = columnPosRight[cell + (colspan > 1 ? colspan - 1 : 0)],
+ scrollRight = scrollLeft + viewportW;
- if (left < scrollLeft) {
- $viewport.scrollLeft(left);
- } else if (right > scrollRight) {
- $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth));
- }
+ if (left < scrollLeft) {
+ $viewport.scrollLeft(left);
+ handleScroll();
+ render();
+ } else if (right > scrollRight) {
+ $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth));
+ handleScroll();
+ render();
+ }
+ }
+
+ function scrollActiveCellIntoView() {
+ if (activeRow != null && activeCell != null) {
+ scrollCellIntoView(activeRow, activeCell);
}
}
@@ -2097,7 +2342,6 @@ if (typeof Slick === "undefined") {
}
if (activeCellChanged) {
- scrollActiveCellIntoView();
trigger(self.onActiveCellChanged, getActiveCell());
}
}
@@ -2347,8 +2591,11 @@ if (typeof Slick === "undefined") {
var colspan = (columnData && columnData.colspan);
if (colspan === "*") {
colspan = columns.length - cell;
+ } else {
+ colspan = colspan || 1;
}
- return (colspan || 1);
+
+ return colspan;
}
function findFirstFocusableCell(row) {
@@ -2559,6 +2806,7 @@ if (typeof Slick === "undefined") {
if (pos) {
var isAddNewRow = (pos.row == getDataLength());
scrollRowIntoView(pos.row, !isAddNewRow);
+ scrollCellIntoView(pos.row, pos.cell);
setActiveCellInternal(getCellNode(pos.row, pos.cell), isAddNewRow || options.autoEdit);
activePosX = pos.posX;
} else {
@@ -2585,6 +2833,7 @@ if (typeof Slick === "undefined") {
}
scrollRowIntoView(row, false);
+ scrollCellIntoView(row, cell);
setActiveCellInternal(getCellNode(row, cell), false);
}
@@ -2647,6 +2896,7 @@ if (typeof Slick === "undefined") {
}
scrollRowIntoView(row, false);
+ scrollCellIntoView(row, cell);
var newCell = getCellNode(row, cell);
@@ -2778,7 +3028,7 @@ if (typeof Slick === "undefined") {
s += ("\n" + "n(umber of pages): " + n);
s += ("\n" + "(current) page: " + page);
s += ("\n" + "page height (ph): " + ph);
- s += ("\n" + "scrollDir: " + scrollDir);
+ s += ("\n" + "vScrollDir: " + vScrollDir);
alert(s);
};
@@ -2862,6 +3112,7 @@ if (typeof Slick === "undefined") {
"updateRowCount": updateRowCount,
"scrollRowIntoView": scrollRowIntoView,
"scrollRowToTop": scrollRowToTop,
+ "scrollCellIntoView": scrollCellIntoView,
"getCanvasNode": getCanvasNode,
"focus": setFocus,
View
14 tests/scrolling benchmarks.html
@@ -39,7 +39,7 @@
<script>
var grid;
var data = [];
- var extraColumns = 0;
+ var extraColumns = 100;
function testPostRender(cellNode, row, dataContext, colDef) {
cellNode.style.backgroundColor = 'yellow';
@@ -83,6 +83,18 @@
}
+ data.getItemMetadata = function (row) {
+ if (row % 2 === 1) {
+ return {
+ "columns": {
+ "duration": {
+ "colspan": 3
+ }
+ }
+ };
+ }
+ };
+
// initialize the grid
grid = new Slick.Grid("#myGrid", data, columns, options);
Please sign in to comment.
Something went wrong with that request. Please try again.