42

I have a table that I'm trying to get sticky headers, following this article. Except my table style has the headers with a border at the top and bottom.

The part I do not understand is that the top/bottom borders applied to the th scroll away with the rest of the table instead of staying with the "stuck" header cells. Is there anything that can be done about this?

Working sample can be found here: https://codepen.io/smlombardi/pen/MNKqQY?editors=1100#0

let string = ''
console.log("oj")
for(let i=0; i<100; i++) {
  string += `
        <tr>
          <td>Malcolm Reynolds</td>
          <td>Captain</td>
          <td>9035749867</td>
          <td>mreynolds</td>
        </tr>
  `
}
document.getElementsByTagName('tbody')[0].innerHTML += string
.table-sticky-container {
  height: 200px;
  overflow-y: scroll;
  border-top: 1px solid green;
  border-bottom: 1px solid green;
}

.table-sticky {

  th {
    position: sticky;
    top: 0;
    z-index: 2;
    border-top: 1px solid red !important;
    border-bottom: 2px solid red !important;
  }
}
<div class="table-sticky-container">
  <table class="table table-sticky">
    <thead class="thead-light">
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Title</th>
        <th scope="col">ID</th>
        <th scope="col">Username</th>
      </tr>
    </thead>
    <tbody>
      <tr>
          <td>Malcolm Reynolds</td>
          <td>Captain</td>
          <td>9035749867</td>
          <td>mreynolds</td>
        </tr>
    </tbody>
  </table>
</div>
MefAldemisov
  • 867
  • 10
  • 21
Steve
  • 14,401
  • 35
  • 125
  • 230
  • Does this answer your question? [Border style do not work with sticky position element](https://stackoverflow.com/questions/50361698/border-style-do-not-work-with-sticky-position-element) – DisgruntledGoat Feb 07 '23 at 16:39

4 Answers4

55

You can add

.table {
  border-collapse: separate;
}

But unfortunately it looks bad, a better solution will be adding a workaround using a pseudo-element.

th:after,
th:before {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
}

th:before {
  top: -1px;
  border-top: 1px solid red;
}

th:after {
  bottom: -1px;
  border-bottom: 2px solid red;
}

.table-sticky-container {
  height: 200px;
  overflow-y: scroll;
  border-top: 1px solid green;
  border-bottom: 1px solid green;
}

.table-sticky th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 2;
}

th:after,
th:before {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
}

th:before {
  top: -1px;
  border-top: 1px solid red;
}

th:after {
  bottom: -1px;
  border-bottom: 2px solid red;
}
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css'>

<div class="table-sticky-container">
    <table class="table table-sticky">
      <thead class="thead-light">
        <tr>
          <th scope="col">Name</th>
          <th scope="col">Title</th>
          <th scope="col">ID</th>
          <th scope="col">Username</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Malcolm Reynolds</td>
          <td>Captain</td>
          <td>9035749867</td>
          <td>mreynolds</td>
        </tr>
        <tr>
          <td>Zoe Washburne</td>
          <td>First Officer</td>
          <td>8908980980</td>
          <td>zwashburne</td>
        </tr>
        <tr>
          <td>Kaylee Frye</td>
          <td>Engineer</td>
          <td>6678687678</td>
          <td>kfrye</td>
        </tr>
        <tr>
          <td>Malcolm Reynolds</td>
          <td>Captain</td>
          <td>9035749867</td>
          <td>mreynolds</td>
        </tr>
        <tr>
          <td>Zoe Washburne</td>
          <td>First Officer</td>
          <td>8908980980</td>
          <td>zwashburne</td>
        </tr>
        <tr>
          <td>Kaylee Frye</td>
          <td>Engineer</td>
          <td>6678687678</td>
          <td>kfrye</td>
        </tr>
      </tbody>
    </table>
  </div>

The second solution

.table {
  border-collapse: collapse;
}

.table-sticky thead th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 2;
  box-shadow: inset 0 1px 0 red, inset 0 -2px 0 red;
}
Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Grzegorz T.
  • 3,903
  • 2
  • 11
  • 24
  • 2
    that works. I notice somehow I can see the content "behind" my header in the gap where the border "should" be. So I set the "top" and "bottom" to -1px. – Steve Jul 23 '19 at 18:56
  • 2
    I added a second solution width box-shadow. – Grzegorz T. Jul 23 '19 at 19:00
  • 1
    I understand graphical purists ;) – Grzegorz T. Jul 23 '19 at 19:42
  • 2
    As @sarthakc suggested, combined the 1st solution with `border-spacing: 0;` and it looks good ! – Sebastien Apr 29 '20 at 09:45
  • Whats' wrong with first solution, in my project I have everything the same, but when I scroll site thead is sticky but background-color of thead disappear – Robert Głowacki Sep 30 '20 at 14:34
  • Thank you for a great solution. It's the only one out of many I've found that works correctly with `rowspan` and `colspan` inside the `thead` (only after adding `th {position: relative}` to the code though). – tomasz86 Aug 11 '22 at 11:59
34

You can add

.table {
  border-collapse: separate;
  border-spacing: 0;
}
SarthakC
  • 455
  • 4
  • 6
5

Don't use border-collapse and draw the lines as follows :

td, th {
    border-bottom: 1px solid grey;
    border-right: 1px solid grey;
}

table {
    border-spacing: 0px;
    border-left: 1px solid grey
}

th {
    background-color:#c5e8ec;
    position: sticky;
    position: -webkit-sticky;
    border-top: 1px solid grey;
    top:0;
}
Littm
  • 4,923
  • 4
  • 30
  • 38
Neklar
  • 51
  • 1
  • 1
0

Another working solution (tested at latest Chrome and FF) - wrap th/td content with divs and use those divs borders instead of cell's borders.

<div class="wrapper">
  <table>
  <thead>
    <tr>
      <th>
        <div class="cell">head1</div>
      </th>
      <th>
        <div class="cell">head2</div>
      </th>
      <th>
        <div class="cell">head3</div>
      </th>
      <th>
        <div class="cell">head4</div>
      </th>
      <th>
        <div class="cell">head5</div>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
    </tr>
    <tr>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
    </tr>
    <tr>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
      <td>
        <div class="cell">1</div>
      </td>
    </tr>
  </tbody>
</table>
</div>

table {
  border-collapse: collapse;
  table-layout: fixed;
  width: 500px;
}

.wrapper {
  height: 150px;
  overflow: auto;
}

td, th {
  width: 150px;
  height: 50px;
  text-align: center;
  font-size: 32px;
  padding: 0;
}

th {
  position: sticky;
  top: 0;
  left: 0;
  background-color: #fff;
}

.cell {
  border: 1px solid black;
  height: 100%;
}

td .cell {
  border-top: none;
  border-left: none;
}

th .cell {
  border-left: none;
}

td:first-child .cell,
th:first-child .cell {
  border-left: 1px solid black;
}