如何基于表格宽度按百分比设置td与th的宽度?固定表头滚动表格列宽失效及自动适配列数解决方案
Problem Description
I've implemented a table with a fixed header and scrollable tbody, but setting width: 50% on <th> and <td> isn't working as expected. Here's my code:
<div id="wrapper"> <table> <thead> <tr> <th>column1</th> <th>column2</th> </tr> </thead> <tbody> <tr> <td>row1</td> <td>row1</td> </tr> <tr> <td>row2</td> <td>row2</td> </tr> <tr> <td>row3</td> <td>row3</td> </tr> <tr> <td>row4</td> <td>row4</td> </tr> </tbody> </table> </div>
#wrapper { width: 100%; } table { border: 1px solid black; width: 100%; table-layout: fixed; } th, td { width: 50%; border: 1px solid black; } thead > tr { position: relative; display: block; } tbody { display: block; height: 80px; overflow: auto; }
Two questions:
- Why isn't the 50% column width taking effect?
- How can I make columns automatically divide the table width evenly if I add a 3rd (or more) columns later?
Answers
1. Why the 50% width isn't working
The core problem is that you’ve set <thead> tr and <tbody> to display: block. This breaks the table’s native layout system—tables depend on their internal elements (thead, tbody, tr, th, td) using their default table-specific display values (like table-header-group, table-row) to keep columns aligned and widths synchronized.
When you switch these elements to block, the <th> and <td> lose their connection to the unified table column structure. Their width: 50% now calculates relative to their immediate block parent (either <thead> or <tbody>) instead of the full table width, and there’s no mechanism left to keep header and body columns matched.
2. Fixed header + dynamic equal-width columns solution
Instead of using display: block to force scrolling, use position: sticky on the table headers. This preserves the table’s native layout, so column widths will automatically sync and adjust as you add more columns. Here’s the revised code:
CSS:
#wrapper { width: 100%; } table { border: 1px solid black; width: 100%; table-layout: fixed; /* Guarantees equal column widths */ border-collapse: collapse; /* Optional: cleans up border spacing */ } th, td { border: 1px solid black; padding: 8px; /* Optional: adds padding for readability */ } th { position: sticky; top: 0; background: white; /* Prevents scrollable content from showing through the header */ } tbody { display: block; /* Keeps tbody scrollable */ height: 80px; overflow-y: auto; } tbody tr { display: table; /* Resets row to table layout to match header columns */ width: 100%; table-layout: fixed; /* Matches the main table's layout rule */ }
HTML: (works with any number of columns)
<div id="wrapper"> <table> <thead> <tr> <th>column1</th> <th>column2</th> <th>column3</th> <!-- Add/remove columns here as needed --> </tr> </thead> <tbody> <tr> <td>row1</td> <td>row1</td> <td>row1</td> </tr> <tr> <td>row2</td> <td>row2</td> <td>row2</td> </tr> <!-- More rows --> </tbody> </table> </div>
Key details about this approach:
position: stickyon<th>locks the header at the top while scrolling the tbody, no hacks needed.table-layout: fixedon the main table ensures columns automatically split the full table width evenly—no need to set explicit percentages for each column.- We keep
tbodyasdisplay: blockfor scrolling, but reset<tbody tr>todisplay: tableto maintain alignment with the header columns. - Adding a white background to
<th>prevents scrollable content from peeking through the fixed header.
内容的提问来源于stack exchange,提问作者peckoski

