Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are these HTML column headers misaligned?

After much experimenting, I finally came up with fairly simple HTML that displays a table the way I want: 6 columns with specific widths, with two columns right-justified, scrolling under a fixed header row. However, the headers don't quite align to the columns:

HTML table

How can I make the headers line up exactly with the data columns? My online searches have found this to be a common problem, but with no real explanation of the cause or a known simple fix.

My HTML is below. The columns widths are are magic numbers because of the data that will eventually be displayed. Making the header text normal instead of bold, or even empty headers, has no effect on alignment.

If besides solving the alignment issue you also have a more simple way of defining a table with the same features, please let me know.

Edit: box-sizing: border-box; as recommended by ProllyGeek seemed promising because it works on the sample data above, but using different cell data still causes alignment to be slightly off as shown here (the Price/Drops, Drops/Description, and Item#/Posted column borders are off even when using border-box):

retried with other data

There are dozens if not hundreds of posts wanting sticky headers over scrollable rows, but apparently all solutions avoid dynamic columns, often using color to hide the alignment issue, or an enclosing div that has to know the table width to show a scrollbar properly positioned alongside dynamic column widths. Most examples ignore column widths and just use 100% wide tables with oversized columns.

There appears to be no known automatic solution 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'm just going to hide misalignments using thead { background-color: black; color: white; }.

<style>

table {
    width: 688px; /* 688 = column widths 80 + 56 + 280 + 120 + 56 + 96 */
    table-layout: fixed;
    font: 12px Courier;
    border-collapse: collapse;
}

table, th, td {
  border: 1px solid black;
  padding: 5px;
}

tbody {
  display: block;
  width: 703px; /* 703 = 688 table width + 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; }

</style>

<table>
  <thead>
    <tr>
      <th>Price</th>
      <th>Drops</th>
      <th>Description</th>
      <th>Category</th>
      <th>Item #</th>
      <th>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>
like image 760
Witness Protection ID 44583292 Avatar asked Sep 28 '20 18:09

Witness Protection ID 44583292


People also ask

How do I fix column headers?

Select the column that's immediately to the right of the last column you want frozen. Select the View tab, Windows Group, click the Freeze Panes drop down and select Freeze Panes. Excel inserts a thin line to show you where the frozen pane begins.

How do you align columns in HTML?

Set a to zero and b to the position of the column in the table, e.g. td:nth-child(2) { text-align: right; } to right-align the second column. If the table does use a colspan attribute, the effect can be achieved by combining adequate CSS attribute selectors like [colspan=n] , though this is not trivial.

What is the alignment of column headings?

In general, Excel is considered the grand-daddy of data grid applications, and most apps with data tables generally follow it's basic defaults. So, titles, dates and text usually align to the left and numerical values generally align to the right. Column headers always align to the left regardless of data type.

How do I fix a header row in HTML table?

To freeze the row/column we can use a simple HTML table and CSS. HTML: In HTML we can define the header row by <th> tag or we can use <td> tag also. Below example is using the <th> tag. We also put the table in DIV element to see the horizontal and vertical scrollbar by setting the overflow property of the DIV element.


1 Answers

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>
like image 84
joe snyder Avatar answered Sep 21 '22 11:09

joe snyder