Why are these HTML column headers misaligned?
This is not a solution to your problem with fixed widths, but a step toward your desire for
a sticky header, with dynamic column widths (no declared table width), over scrollable rows, with precise header and column alignment, using just pure CSS/HTML.
I believe it's faster for large tables to be rendered with dynamic column widths,
header/column alignment is then guaranteed,
plus the code is no longer cluttered with magic numbers.
Adding scrolling requires using an enclosing <div>
like the snippet below.
UPDATE: Per your comment request, here's a dynamic table (no declared column widths) with a scrolling div that automatically repositions the scrollbar as necessary. Click the "Refill table" button to generate test tables.
const SCROLLBARWIDTH = 16;
let maxWidth = 5; // initial max width of our dummy strings
function dummyString() {
return 'x'.repeat(Math.floor((Math.random() * maxWidth)+1)); // dummy data that will tend to widen with each refill
}
function refill() {
document.getElementById("theBody").innerHTML = '<tr></tr>'; // remove any previous table body
let tableRef = document.getElementById("theTable"); // our scrollable HTML table that we'll add rows to
for (let i = 0; i < 20; i++) { // create 20 rows of dummy data
let newRow = tableRef.insertRow(-1); // add a new row to the table, then set the 6 <td> cells...
newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // price
newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // drops
newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // description
newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // category
newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // item #
newRow.insertCell(-1).appendChild(document.createTextNode(dummyString())); // posted
}
let tableWidth = window.getComputedStyle(tableRef).getPropertyValue('width'); // get resulting dynamic table width
document.getElementById("container").style.width = parseInt(tableWidth) + 1 + SCROLLBARWIDTH + 'px'; // make scrolling div wide enough
maxWidth++; // so our next refill will tend to have wider dummy strings
}
refill();
#container { overflow-y: scroll; height: 200px; } /* width to be determined after table filled */
thead th { position: sticky; top: 0; }
table { font: 12px Courier; border-collapse: collapse; }
th { background: black; color: white; } /* hide scrolling behind the header */
table, th, td { border: 1px solid black; padding: 5px; }
<button onclick="refill()">Refill table</button> <!-- click to generate wider table -->
<div id="container">
<table id="theTable">
<thead>
<tr>
<th>Price</th>
<th>Drops</th>
<th>Description</th>
<th>Category</th>
<th>Item #</th>
<th>Posted</th>
</tr>
</thead>
<tbody id="theBody">
<tr>
</tr>
</tbody>
</table>
</div>
I would suggest using min, max-width instead of width. Also, you added all the column width, but every column has a padding of 5px, which adds 5px on left and 5px on right. So, make sure you add them up.
Here is the fix:
table {
/* width: 688px; 688 = column widths 80 + 56 + 280 + 120 + 56 + 96 + 60(padding)*/
min-width: 748px;
max-width: 763px;
table-layout: fixed;
font: 12px Courier;
border-collapse: collapse;
}
table,
th,
td {
border: 1px solid black;
padding: 5px;
}
tbody {
display: block;
/* max-width: 763px; */
/* 763 = 688 table width + 60(padding) + 15 extra for scrollbar */
overflow-y: scroll;
height: 65px;
}
thead tr {
display: block;
}
tr:nth-child(even) {
background-color: #eee;
}
/* th:nth-child(1), td:nth-child(1) {text-align: right; width: 80px; }
th:nth-child(2), td:nth-child(2) {text-align: left; width: 56px; }
th:nth-child(3), td:nth-child(3) {text-align: left; width: 280px; }
th:nth-child(4), td:nth-child(4) {text-align: left; width: 120px; }
th:nth-child(5), td:nth-child(5) {text-align: right; width: 56px; }
th:nth-child(6), td:nth-child(6) {text-align: left; width: 96px; } */
th:nth-child(1),
td:nth-child(1) {
text-align: right;
min-width: 80px;
max-width: 80px;
}
th:nth-child(2),
td:nth-child(2) {
text-align: left;
min-width: 56px;
max-width: 56px;
}
th:nth-child(3),
td:nth-child(3) {
text-align: left;
min-width: 280px;
max-width: 280px;
}
th:nth-child(4),
td:nth-child(4) {
text-align: left;
min-width: 120px;
max-width: 120px;
}
th:nth-child(5),
td:nth-child(5) {
text-align: right;
min-width: 56px;
max-width: 56px;
}
th:nth-child(6),
td:nth-child(6) {
text-align: left;
min-width: 96px;
max-width: 96px;
}
.lastColumn {
min-width: 114px !important;
}
<table>
<thead>
<tr>
<th>Price</th>
<th>Drops</th>
<th>Description</th>
<th>Category</th>
<th>Item #</th>
<th class="lastColumn">Posted</th>
</tr>
</thead>
<tbody>
<tr>
<td>$5.00
<td>Oct 10
<td>Valuable item
<td>Miscellaneous
<td>1234
<td>Sep 10 2020
</tr>
<tr>
<td>$5.00
<td>Oct 10
<td>Valuable item
<td>Miscellaneous
<td>1234
<td>Sep 10 2020
</tr>
<tr>
<td>$5.00
<td>Oct 10
<td>Valuable item
<td>Miscellaneous
<td>1234
<td>Sep 10 2020
</tr>
<tr>
<td>$5.00
<td>Oct 10
<td>Valuable item
<td>Miscellaneous
<td>1234
<td>Sep 10 2020
</tr>
</tbody>
</table>
You just need the last column to take the remaining width. So, specify to that column.