Handsontable example
HTML
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/handsontable.full.min.js"></script>
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/styles/handsontable.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/styles/ht-theme-main.css" />
<script src="https://handsontable.com/docs/scripts/fixer.js"></script>
<div class="fixed-header">
<h3>My Fixed Navigation (50px height)</h3>
</div>
<div id="example1"></div>
<div style="padding: 20px;">
<h1>HEADER</h1>
<p>Some text here. Some text here. Some text here. Some text here. Some text here. Some text here. Some text here.</p>
</div>
<script type="module" src="script.js"></script>
</body>
</html>
SCSS
body {
margin: 0;
padding: 0;
}
.fixed-header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 50px;
background: #333;
color: white;
display: flex;
align-items: center;
padding: 0 20px;
z-index: 1000;
}
/* Container for proper spacing */
#example1 {
margin: 20px;
margin-top: 70px; /* 50px nav + 20px spacing */
}
/* Fix the column headers at 50px */
#example1 .ht_clone_top {
position: fixed !important;
top: 50px !important;
z-index: 102 !important;
}
/* Fix the corner (where row headers meet column headers) */
#example1 .ht_clone_top_inline_start_corner {
position: fixed !important;
top: 50px !important;
z-index: 103 !important;
}
/* Ensure row headers are visible */
#example1 .ht_clone_inline_start {
z-index: 101 !important;
}
/* Hide the original column headers in the master table to remove the gap */
#example1 .ht_master thead {
display: none !important;
}
/* Also hide column headers in the inline start clone (row headers area) */
#example1 .ht_clone_inline_start thead {
display: none !important;
}
/* Remove any bottom border/margin from fixed headers */
#example1 .ht_clone_top,
#example1 .ht_clone_top_inline_start_corner {
margin-bottom: 0 !important;
border-bottom: none !important;
}
/* Remove any top border/margin from the master table body */
#example1 .ht_master tbody tr:first-child td,
#example1 .ht_clone_inline_start tbody tr:first-child th {
border-top: none !important;
}
Babel + JSX
import Handsontable from 'handsontable/base';
import { registerAllModules } from 'handsontable/registry';
import 'handsontable/styles/handsontable.css';
import 'handsontable/styles/ht-theme-main.css';
// Register all Handsontable's modules
registerAllModules();
// Generate an array of arrays with dummy data
const data = new Array(100) // number of rows
.fill(null)
.map((_, row) =>
new Array(50) // number of columns
.fill(null)
.map((_, column) => `${row}, ${column}`)
);
const container = document.querySelector('#example1');
const hot = new Handsontable(container, {
themeName: 'ht-theme-main',
data,
height: 'auto',
rowHeaders: true,
colHeaders: true,
contextMenu: true,
autoWrapRow: true,
autoWrapCol: true,
licenseKey: 'non-commercial-and-evaluation',
});
// Synchronize horizontal scrolling
hot.addHook('afterScrollHorizontally', function() {
const masterHolder = hot.view._wt.wtTable.holder;
const topClone = container.querySelector('.ht_clone_top .wtHolder');
if (topClone) {
topClone.scrollLeft = masterHolder.scrollLeft;
}
});
// Update fixed header positions
function updateHeaderPositions() {
const containerRect = container.getBoundingClientRect();
const topClone = container.querySelector('.ht_clone_top');
const cornerClone = container.querySelector('.ht_clone_top_inline_start_corner');
if (topClone && containerRect) {
topClone.style.left = `${containerRect.left}px`;
topClone.style.width = `${containerRect.width}px`;
}
if (cornerClone && containerRect) {
cornerClone.style.left = `${containerRect.left}px`;
}
}
// Initialize
setTimeout(updateHeaderPositions, 100);
window.addEventListener('resize', updateHeaderPositions);
window.addEventListener('scroll', updateHeaderPositions);