Fixing Page Jumping When Adding Rows to Nested Tabulator Sub-Tables
Why Does Tabulator Jump to the Top When Updating Sub-Tables?
When working with nested Tabulator instances—such as category master tables containing child tables for line items—a common frustration is unexpected page scrolling. Specifically, calling addData() or addRow() on a child table partway down a long page causes the entire browser window to jump straight back to the top.
This jump happens due to Tabulator's Virtual DOM renderer. When you define a height property (even a relative value like height: "100%"), Tabulator automatically enables virtual rendering. When a nested table mutates and expands its DOM footprint, Tabulator's layout engine triggers a recalculation and redraw. Because the parent table does not have a constrained pixel height with its own internal scrollbar, the virtual scroll calculations misalign, causing the viewport to reset to coordinate 0.
Solution 1: Disable the Virtual DOM by Removing height (Recommended)
If your goal is to let the table expand naturally down the page without any internal scrollbars, the cleanest and most performant solution in Tabulator is to completely omit the height property or set it to false. This switches Tabulator into standard vertical rendering mode (renderVertical: "basic"), meaning Tabulator will render standard table rows without trying to compute virtual scrolling offsets.
var table = new Tabulator("#pricingGrid", {
// height: "100%", // <-- REMOVE or comment out this line
columns: columns,
placeholder: "No Data Available",
rowFormatter: function(row) {
var holderEl = document.createElement("div");
var tableEl = document.createElement("div");
holderEl.appendChild(tableEl);
row.getElement().appendChild(holderEl);
var subTable = new Tabulator(tableEl, {
// height: "100%", // <-- REMOVE this as well
data: row.getData().items,
columns: [
{ title: "Item", field: "item" }
]
});
row.getData().subTable = subTable;
}
});By removing the height declaration from both parent and child configurations, the browser manages the document flow naturally, completely eliminating the scroll-to-top bug.
Solution 2: Use addRow() Instead of addData()
When appending a single record to a table, using addRow() is typically preferred over addData(). addData() performs an array-level sync which often triggers a full re-render of the table, whereas addRow() inserts a single DOM node at a specified position.
You can also capture and lock the user's scroll position while appending data:
var rowTools = function (cell, formatterParams, onRendered) {
var button = document.createElement("button");
button.textContent = "Add Item";
button.addEventListener("click", function (event) {
event.preventDefault();
event.stopPropagation(); // Prevent bubbling to parent table elements
var subTable = cell.getRow().getData().subTable;
if (subTable) {
var currentScrollY = window.scrollY;
// Add the new row at the bottom of the sub-table
subTable.addRow({ item: "New Item" }, false).then(function() {
// Re-anchor scroll position in case of layout shifts
window.scrollTo(0, currentScrollY);
});
}
});
return button;
};Solution 3: Explicitly Set renderVertical: "basic"
If your project setup or CSS framework enforces a 100% height container and you cannot simply remove the height option, you can explicitly force Tabulator to disable virtual scrolling by setting the render mode to basic:
var table = new Tabulator("#pricingGrid", {
renderVertical: "basic",
columns: columns,
// ... remaining configuration
});With renderVertical: "basic", all rows are rendered into the DOM at once. While this requires caution if your dataset contains thousands of rows, for quotation lists and grouped categories it works seamlessly and prevents jumpy layout behaviors.
Summary
- The Cause:
height: "100%"activates virtual DOM calculations that fail when child tables change dimension without an explicit pixel container. - The Fix: Remove
heightconfigurations on both parent and child tables, or setrenderVertical: "basic". - Refinement: Switch to
subTable.addRow()withevent.stopPropagation()to avoid unnecessary complete re-renders.