In one of my recent projects, I faced the problem of having to visually align corresponding rows between multiple HTML tables to make them behave as if they were one. These tables contained rows of content to be compared with the corresponding rows at the same index in the other tables. Unfortunately, some of this content could be larger than others, causing the tables to become unaligned at some point. The goal was then to make the tables align dynamically with respect to the larger content as outlined in the two pictures below.
👆 how it looks
how it should look like 👇
After a bit of googling thinking hard I found a relatively simple way to solve it with React using a combination of createRef
, useLayoutEffect
and clientHeight
.
Basically, the idea is to assign a ref
to each row and - inside useLayoutEffect
, just before the browser repaints the screen - determine the row with the tallest height between all tables at the same index by examining the clientHeight
property. Then, assign this maximum height table-wide to all rows with the same index.
So, let’s create the refs for the rows in each table,
const rows = content => content.map(data => ({ data, ref: createRef() }));
const tables = [
rows(["content", "content", "content"]),
rows(["content", "so much many more large content", "content"])
];
link them to their corresponding table row
{tables.map(table => (
<table>
<tbody>
{table.map(row => (
<tr ref={row.ref}>
<td>{row.data}</td>
</tr>
))}
</tbody>
</table>
))}
and find the row with the tallest height between all rows at the same index and adapt for all.
useLayoutEffect(() => {
for (const i of Array(rowsCount).keys()) {
const max = Math.max(
...tables.map(table => table[i].ref.current.clientHeight)
);
tables.forEach(table => {
table[i].ref.current.style.height = `${max}px`
});
}
});
You can find the fully working example on CodeSandbox by clicking the button below.
Just wanted to share this idea with you real quick.
In my actual project I extended that code a bit to also handle window resize events and shrinking content and was left with a pretty simple yet stable solution. Hit me up if you want more details on how I did that!