For whatever it's worth now: here is yet another solution:
- create two divs within a
display: inline-block
- in the first div, put a table with only the header (header table
tabhead
)
- in the 2nd div, put a table with header and data (data table / full table
tabfull
)
- use JavaScript, use
setTimeout(() => {/*...*/})
to execute code after render / after filling the table with results from fetch
- measure the width of each th in the data table (using
clientWidth
)
- apply the same width to the counterpart in the header table
- set visibility of the header of the data table to hidden and set the margin top to -1 * height of data table thead pixels
With a few tweaks, this is the method to use (for brevity / simplicity, I used d3js, the same operations can be done using plain DOM):
setTimeout(() => { // pass one cycle
d3.select('#tabfull')
.style('margin-top', (-1 * d3.select('#tabscroll').select('thead').node().getBoundingClientRect().height) + 'px')
.select('thead')
.style('visibility', 'hidden');
let widths=[]; // really rely on COMPUTED values
d3.select('#tabfull').select('thead').selectAll('th')
.each((n, i, nd) => widths.push(nd[i].clientWidth));
d3.select('#tabhead').select('thead').selectAll('th')
.each((n, i, nd) => d3.select(nd[i])
.style('padding-right', 0)
.style('padding-left', 0)
.style('width', widths[i]+'px'));
})
Waiting on render cycle has the advantage of using the browser layout engine thoughout the process - for any type of header; it's not bound to special condition or cell content lengths being somehow similar. It also adjusts correctly for visible scrollbars (like on Windows)
I've put up a codepen with a full example here: https://codepen.io/sebredhh/pen/QmJvKy