I was tasked with creating a table that would be used as the major portion of a layout. The header of the table should be static while body should scroll if necessary. The issue is that if the scrollbar is needed, the columns of the table becomes misaligned because the width of the tbody changes.
I used a bit of javascript to compensate by setting the right padding on the thead to be equal to the width of the scrollbar, and planned to use a listener to remove / add back the padding as the scrollbar disappeared / appeared.
While this approach worked to keep the columns aligned, I was asked to come up with an HTML/CSS only solution if possible. Does anyone know of a way to achieve this without any js? Thanks.
Some relevant CSS I currently have:
table {
width: 100%;
border-collapse: collapse;
}
table tbody {
position: absolute;
top: 24px;
left: 0;
right: 0;
bottom: 0;
overflow-y: auto;
}
table thead {
position: absolute;
top: 0;
left: 0;
right: 0;
}
Here is a fiddle to see the whole example: http://jsfiddle.net/kirky93/qqv73kjo/5/
Drag the viewport up/down to make the scrollbar dis/appear to see the issue.
To eliminate the misalignment with pure CSS, we can get the scrollbar to push the header aside. We can do this by making it position: fixed so that the scroll bar on the tbody extends to the top of the viewport.
Too much to read? Skip to the example at the bottom.
Column colors and widths
Use seven <col> elements, one to represent each column. We can place the class on them with width and background color and these properties will be repeated for the entire column without having to put a class on each table cell. They look like this:
<table class="fixed">
<col class="one">
<col class="two">
<col class="three">
<col class="four">
<col class="five">
<col class="six">
<col class="seven">
In order to place a width CSS property on the columns, we can set table-layout: fixed on the table element. No matching min/max widths are needed now.
Getting the thead to scroll
Set position: fixed
Set display: table to make it behave like a table again
Set table-layout: fixed so that the width behave properly
Because the thead is now effectively removed from its table and <col> elements, set the column classes on each th
The tbody
Leave it at its initial position: static
Set a top margin the same height as the thead
Full Example
Note the box-sizing: border-box which makes elements include padding and borders into their widths and heights.
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
}
table {
border-collapse: collapse;
background: #FFF;
table-layout: fixed;
width: 100%;
}
table thead {
position: fixed;
top: 0;
left: 0;
width: 100%;
background: #FFF;
display: table;
table-layout: fixed;
border: solid 1px #000;
}
table tbody {
margin-top: 24px;
}
table {
/*Make sure table has border that matches the cell border so it is included in the width*/
border: 1px solid black;
}
td,
th {
height: 20px;
border: 1px solid black;
}
.one {
width: 30px;
background-color: yellow;
}
.two {
width: 30px;
background-color: orange;
}
.three {
width: 30px;
background-color: red;
}
.four {
width: 100%;
}
.five {
width: 100px;
background-color: green;
}
.six {
width: 100px;
background-color: lightblue;
}
.seven {
width: 100px;
}
<table class="fixed">
<col class="one">
<col class="two">
<col class="three">
<col class="four">
<col class="five">
<col class="six">
<col class="seven">
<thead>
<tr>
<th class="one">1</th>
<th class="two">2</th>
<th class="three">3</th>
<th class="four">4</th>
<th class="five">5</th>
<th class="six">6</th>
<th class="seven">7</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
</tr>
</tbody>
</table>
just set it to overflow:scroll on the tbody instead of auto
table tbody {
position: absolute;
top: 24px;
left: 0;
right: 0;
bottom: 0;
overflow-y: scroll;
}
Related
I'm currently building a web app with vuetify, and i'm having an issue with a table that is taking me a lot of time. I'd like to have like a scroller on mobile that allow you to see the table while keeping the aspect ratio and not stretching itself. I'll post the code down here and a picture of how the table is showed in a mobile view. I already tried adding overflow: auto and overflow: scroll but nothing it's changing. Can you help me?
Code:
<table id="tabellaPunteggi" style="overflow-x:auto;">
<tbody>
<tr>
<td class="remove" colspan="3">Buca</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
<td>16</td>
<td>17</td>
<td>18</td>
<td>=</td>
<td>+/-</td>
</tr>
<tr>
<td class="remove" colspan="3">PAR</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>4</td>
<td>4</td>
<td>5</td>
<td>3</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>4</td>
<td>4</td>
<td>5</td>
<td>3</td>
<td>72</td>
<td></td>
</tr>
<tr>
<td class="remove" colspan="3">Score</td>
<td>4</td>
<td>4</td>
<td>5</td>
<td>5</td>
<td>3</td>
<td>7</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>9</td>
<td>4</td>
<td>3</td>
<td>5</td>
<td>4</td>
<td>9</td>
<td>5</td>
<td>3</td>
<td>3</td>
<td></td>
<td>-9</td>
</tr>
</tbody>
</table>
<!-- Here is the table -->
And the css i tried using:
table#tabellaPunteggi{
margin: 0 auto;
border-collapse: collapse;
}
table#tabellaPunteggi tr td:not(.remove){
width: 45px;
height:45px;
}
table#tabellaPunteggi tr td {
border: solid 1px black;
}
table#tabellaPunteggi tr td.remove {
width: 75px;
}
Any help is appreciated!
Thanks for your time! Have a nice day!
Edit (28/01/2022):
I tried using your answers (i really appreciate them), but there is like another problem.
I screenshot it so you can see clearly at this link: https://imgur.com/a/AQfQdOz
Thanks for your help!
Set a min width on the td
you don't need the :not(.remove) since you set the width for .remove afterwards.
table#tabellaPunteggi tr td{
min-width: 45px;
height: 45px;
}
table#tabellaPunteggi tr td.remove {
min-width: 75px;
}
This will keep things as is
just update your this css lines as i updated for you
table#tabellaPunteggi{ border-collapse: collapse; overflow: hidden; min-width:500px }
table {
border:1px solid red;
width:100%;
position: relative;
}
table tbody {
height:100px;
overflow-y:auto;
display:block;
}
table tbody tr {
display:table;
width:100%;
}
td,th {
text-align: center;
}
<div>
<table>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr><tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr><tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
</tbody>
</table>
</div>
Some reasons, the tbody not expanding with 100%. any one figure-out the issue?
I can see a couple of problems that causes this:
You have css styling on the table tbody tr to: display: table;
And table tbody { display: block; }
Remove those and the issue should be fixed.
Edit: PS, for keeping the table height and a scroll when needed, I would wrap the table in a div and have the div set to something like:
div.table-wrap {
height: 100px;
overflow-y:auto;
}
Regardless of which browser is rendering, the css rule width: 100% sets the element in question to a width of 100% of the parent element. So the table is 100% of its parent element, whatever that is, and on the tbody you have not set a width, so when you set the width of the rows to 100%, it is 100% of the tbody's width, which the browser will set to whatever it feels is appropriate.
If you want to make sure something is as wide as the viewport, set the width to 100vw, or make sure every parent of the element has width 100% all the way up to the html tag.
And as Vitikka pointed out, you shouln't set display:table on the tr tags.
You can use this example, it will work guessing you are using Bootstrap!
<table style="width:100%;">
Inside the table tag, you can add styles without placing them in your CSS file. Another example using Bootstrap:
<table class="bg-dark text-white" style="width:100%;">
You can also style using the style tag any td or tr tag too.
<th class="text-center text-white" style="text-transform:uppercase;">Content goes here</th>
I got what I want as per the screenshot. However, the borders got attached to one another and got thicker. How do I maintain the border size?
This is actually what I'm planning to make it look like:
DEMO: https://jsfiddle.net/xnqh9d70/
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>Day</td>
<td>Sun.</td>
<td>Mon.</td>
<td>Tues.</td>
<td>Wed.</td>
<td>Thu.</td>
<td>Fri.</td>
<td>Sat.</td>
<td></td>
</tr>
<tr>
<td>Fare(s)</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td></td>
</tr>
<tr>
<td>Date(s)</td>
<td>9</td>
<td>8</td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
</tr>
</tbody>
</table>
CSS:
table {
border-collapse: separate;
empty-cells: hide;
border: 0;
border-color: #000000;
}
This is probably not the nicest solution, but what you can do is eliminate one of the two borders completely, only having borders on elements where it is needed then.
table {
border-collapse: separate;
empty-cells: hide;
border: 0;
border-color: #000000;
border-left: 1px solid black
}
tr {
border-bottom: none;
}
td {
border-left: none;
}
tr:last-of-type {
border-bottom: 1px solid black;
}
td:first-of-type {
border-right: 1px solid black;
}
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>Day</td>
<td>Sun.</td>
<td>Mon.</td>
<td>Tues.</td>
<td>Wed.</td>
<td>Thu.</td>
<td>Fri.</td>
<td>Sat.</td>
<td></td>
</tr>
<tr>
<td>Fare(s)</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td></td>
</tr>
<tr>
<td>Date(s)</td>
<td>9</td>
<td>8</td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
</tr>
</tbody>
</table>
The general way for creating borders between cells is by styling the td elements, and setting border-collapse: collapse; on the table.
I also added a class for your blank cells to remove the border on them.
table {
empty-cells: hide;
border-collapse: collapse;
border-color: #000000;
}
td {
border: 1px solid black;
}
.empty-cell {
border: none;
}
<table>
<tbody>
<tr>
<td>Day</td>
<td>Sun.</td>
<td>Mon.</td>
<td>Tues.</td>
<td>Wed.</td>
<td>Thu.</td>
<td>Fri.</td>
<td>Sat.</td>
<td class="empty-cell"></td>
</tr>
<tr>
<td>Fare(s)</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td class="empty-cell"></td>
</tr>
<tr>
<td>Date(s)</td>
<td>9</td>
<td>8</td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
</tr>
</tbody>
</table>
empty-cells: hide; is use to hide border
The empty-cells property sets whether or not to display borders on
empty cells in a table. Reference Here
you can apply css to td:empty for hide empty td
and for border-collapse: separate; case you need to add manually border-right to last td
table {
border-collapse: collapse;
border-color: #000000;
empty-cells: hide;
}
td {
border: 1px solid black;
}
td:empty {
border: 0px;
}
<table>
<tbody>
<tr>
<td>Day</td>
<td>Sun.</td>
<td>Mon.</td>
<td>Tues.</td>
<td>Wed.</td>
<td>Thu.</td>
<td>Fri.</td>
<td>Sat.</td>
<td></td>
</tr>
<tr>
<td>Fare(s)</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td></td>
</tr>
<tr>
<td>Date(s)</td>
<td>9</td>
<td>8</td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>4</td>
<td>3</td>
<td>2</td>
</tr>
</tbody>
</table>
I'm trying to center table vertically and horizontally via flexbox.
One "screen" - one table in the middle, scrolling, - yet another table in the middle.
But via this code doesn't work:
* {
margin : 0;
padding : 0;
}
table {
text-align : center;
}
thead {
font-weight : bold;
background : forestgreen;
}
tfoot {
font-weight : bold;
background : tomato;
}
th, td {
width : 5vw;
}
body {
min-height : 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.wrapper {
min-height : 100vh;
}
<body>
<div class="wrapper">
<table>
<thead>
<tj>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
</div><!-- wrapper end -->
<div class="wrapper">
<table>
<thead>
<tj>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
<div><!-- wrapper end -->
</body>
Where I'm wrong?
If to have the table's each centered per full screen/viewport, to scroll between the 2 tables, check sample 1 below, where I moved all properties from the body rule to the .wrapper rule.
If you intended to have the 2 table's on top of each other, check sample 2 below, which have flex-direction: column set.
To center the table's side-by-side, remove min-height: 100vh from the wrapper rule, sample 3.
Sample 1 - one per full screen/viewport
* {
margin : 0;
padding : 0;
}
table {
text-align : center;
}
thead {
font-weight : bold;
background : forestgreen;
}
tfoot {
font-weight : bold;
background : tomato;
}
th, td {
width : 5vw;
}
.wrapper {
min-height : 100vh;
display: flex;
align-items: center;
justify-content: center;
}
<div class="wrapper">
<table>
<thead>
<tr>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
</div><!-- wrapper end -->
<div class="wrapper">
<table>
<thead>
<tr>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
<div><!-- wrapper end -->
Sample 2 - on top of each other
* {
margin : 0;
padding : 0;
}
table {
text-align : center;
}
thead {
font-weight : bold;
background : forestgreen;
}
tfoot {
font-weight : bold;
background : tomato;
}
th, td {
width : 5vw;
}
body {
min-height : 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
<div class="wrapper">
<table>
<thead>
<tr>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
</div><!-- wrapper end -->
<div class="wrapper">
<table>
<thead>
<tr>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
<div><!-- wrapper end -->
Sample 3 - side-by-side
* {
margin : 0;
padding : 0;
}
table {
text-align : center;
}
thead {
font-weight : bold;
background : forestgreen;
}
tfoot {
font-weight : bold;
background : tomato;
}
th, td {
width : 5vw;
}
body {
min-height : 100vh;
display: flex;
align-items: center;
justify-content: center;
}
<div class="wrapper">
<table>
<thead>
<tj>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
</div><!-- wrapper end -->
<div class="wrapper">
<table>
<thead>
<tj>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
</tfoot>
</table>
<div><!-- wrapper end -->
You can also use place-content: center with display: grid to achieve this.
What's place-content ?
From css-tricks.com
The place-content property in CSS is shorthand for the align-content
and justify-content properties, combining them into a single
declaration in CSS Grid and Flexbox layouts, where align-content and
justify-content are values that align an individual item in the block
and inline directions.
body{
margin:0;
text-align:center;
}
.wrapper {
min-height:100vh;
display:grid;
place-content:center;
}
th, td {
width: 30px;
}
<body>
<div class="wrapper">
<table>
<thead style="background:forestgreen">
<tj>
<th>1</th>
<th>1</th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
</tbody>
<tfoot style="background:tomato">
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
</tfoot>
</table>
</div>
</body>
As you might know, position: sticky; has landed in Webkit (demo).
So far I can see this only works within the parent element. But I'd like to know if I can use this in a scrolling div with a table.
So it needs to 'listen' on the scrolling event of the div, not the table.
I know I can do this with javascript and absolute positioning, but I was wondering if the sticky-positioning would support this.
Position sticky on thead th works in 2018!
In your stylesheets just add this one line:
thead th { position: sticky; top: 0; }
Your table will need to include thead and th for this to style.
<table>
<thead>
<tr>
<th>column 1</th>
<th>column 2</th>
<th>column 3</th>
<th>column 4</th>
</tr>
</thead>
<tbody>
// your body code
</tbody>
</table>
Also, if you have multiple rows in thead, you can select the first one to remain sticky:
thead tr:first-child th { position: sticky; top: 0; }
As of March 2018 support is pretty much there across modern browsers
ref: https://caniuse.com/#feat=css-sticky
Credit goes to #ctf0 for this one (ref comment made 3 Dec 2017)
If you need sticky header for chrome only then you can set position: sticky; top: some_value (top property is mandatory) for td element in a thead element.
See:
<table border=1>
<thead>
<tr>
<td style='position: sticky; top: -1px;background: red'>Sticky Column</td>
<td>Simple column</td>
</tr>
</thead>
table with a stiky header
position: sticky doesn't work with table elements (as long as their display attribute starts with table-) since tables are not part of specification:
Other kinds of layout, such as tables, "floating" boxes, ruby annotations, grid layouts, columns and basic handling of normal "flow" content, are described in other modules.
Edit: As Jul 2019 according to https://caniuse.com/#feat=css-sticky Firefox supports this feature and Chrome has at least support for <th> tag.
As it turns out it position: sticky only works in the window and not in a scrolling div.
I created a test-case with a very long table with a table header:
h1 {
font-size: 18px;
font-weight: bold;
margin: 10px 0;
}
div.testTable {
height: 200px;
overflow: auto;
}
table.stickyHead thead {
position: -webkit-sticky;
top: 0px;
background: grey;
}
table.stickyHead td,
table.stickyHead th {
padding: 2px 3px;
}
<h1>Position sticky</h1>
<div class="testTable">
<table class="stickyHead">
<thead>
<tr>
<th>column 1</th>
<th>column 2</th>
<th>column 3</th>
<th>column 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
</tbody>
</table>
</div>
As you can see, if you remove the overflow from the wrapper and make your window not so tall, the table-head is sticking to the top of the window. I doesn't apply to the wrapping div even if you make give the div position: relative
Caution:
position:sticky doesn't work anymore on Google Chrome in 2019, try to use fixed instead or display:inline-block
Setting the position:sticky for the thead is enough, no need to set it for th :
table.StickyHeader thead {
position: sticky;
top: 0px;
}
Tested in:
Edge 105.0.1343.42 ,
Firefox 105.0 ,
Chrome 105.0.5195.127 ,
Opera 91.0.4516.16
But, Firefox can not render borders of th when position of thead is set to sticky and th has background-color:
table.StickyHeader th {
border: 1px solid black;
padding: 5px;
background-color: gold;
text-align: center;
}
Firefox
Edge, Chrome, Opera