28

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
  • 834
  • 10
  • 19
Steve
  • 13,564
  • 28
  • 118
  • 212

5 Answers5

41

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;
 }
Grzegorz T.
  • 3,397
  • 2
  • 10
  • 22
25

You can add

.table {
  border-collapse: separate;
  border-spacing: 0;
}
SarthakC
  • 365
  • 3
  • 6
3

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,903
  • 4
  • 29
  • 37
Neklar
  • 31
  • 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;
}
0

Here is CSB for mui material table, tested in Chrome, FF, Edge & Safari

https://codesandbox.io/s/mui-material-table-sticky-header-with-borders-fsmnd?file=/src/App.tsx

In this case key was in unsetting border-collapse on the table:

const TableSticky = (props: TableProps) => {
  const { sx, ...other } = props;
  return (
    <Table
      sx={{
        borderCollapse: "unset",
        borderLeft: border,
        ...sx,
      }}
      size="small"
      {...other}
    />
  );
};

It's also smart to use postion: sticky on header columns instead of the table:

const HeadCell = (props: TableCellProps) => {
  const { sx, ...other } = props;
  const theme = useTheme();

  return (
    <TableCell
      sx={{
        borderBottom: border,
        borderRight: border,
        borderTop: border,
        position: "sticky",
        top: 0,
        backgroundColor: theme.palette.background.default,
        ...sx
      }}
      {...other}
    />
  );
};
keemor
  • 944
  • 12
  • 13