218

I don't know why my border style do not work with position: sticky; attribute. I would like to set border styles on my sticky table header. But I don't want to use the transparent background colour. How can I achieve it? Here are sample codes for my problem and JSFiddle Link

#wrapper {
  width: 400px;
  height: 200px;
  overflow: auto;
}

table {
  width: 100%;
  text-align: center;
  border-collapse: collapse;
}

table tr th,
table tr td {
  border: 2px solid;
}

table thead th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background-color: #edecec;
}
<div id="wrapper">
  <table>
    <thead>
      <tr>
        <th>A</th>
        <th>B</th>
        <th>C</th>
        <th>D</th>
        <th>E</th>
      </tr>
    </thead>
    <tr>
      <td>1</td>
      <td>1</td>
      <td>1</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <td>2</td>
      <td>2</td>
      <td>2</td>
      <td>2</td>
      <td>2</td>
    </tr>
    <tr>
      <td>3</td>
      <td>3</td>
      <td>3</td>
      <td>3</td>
      <td>3</td>
    </tr>
    <tr>
      <td>4</td>
      <td>4</td>
      <td>4</td>
      <td>4</td>
      <td>4</td>
    </tr>
    <tr>
      <td>5</td>
      <td>5</td>
      <td>5</td>
      <td>5</td>
      <td>5</td>
    </tr>
    <tr>
      <td>6</td>
      <td>6</td>
      <td>6</td>
      <td>6</td>
      <td>6</td>
    </tr>
    <tr>
      <td>7</td>
      <td>7</td>
      <td>7</td>
      <td>7</td>
      <td>7</td>
    </tr>
    <tr>
      <td>8</td>
      <td>8</td>
      <td>8</td>
      <td>8</td>
      <td>8</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tbody>
    </tbody>
  </table>
</div>

Below are screenshots for what I want and who do not clear enough my question.

enter image description here

You can see inline border styles of th do not work (remove position attribute of css and you will see boders around.).

enter image description here

After scroll down a little. You will see all border-styles were gone.

Jithin Raj P R
  • 6,667
  • 8
  • 38
  • 69
Cataclysm
  • 7,592
  • 21
  • 74
  • 123

14 Answers14

357

The problem occurs because of the use of border-collapse: collapse. When browsers collapse the borders, the top and bottom border on the <th> must be getting applied to surrounding elements—the top border to the <table> and the bottom border to the following <tr>.

If you use border-collapse: separate and fashion your borders to sit on one side, the borders will truly attach to the <th>, stay fixed as expected, and appear collapsed.

Here are example styles that can be applied to your HTML snippet.

#wrapper {
  width: 100%;
  height: 150px;
  overflow: auto;
}

table {
  width: 100%;
  text-align: center;
  border-collapse: separate; /* Don't collapse */
  border-spacing: 0;
}

table th {
  /* Apply both top and bottom borders to the <th> */
  border-top: 2px solid;
  border-bottom: 2px solid;
  border-right: 2px solid;
}

table td {
  /* For cells, apply the border to one of each side only (right but not left, bottom but not top) */
  border-bottom: 2px solid;
  border-right: 2px solid;
}

table th:first-child,
table td:first-child {
  /* Apply a left border on the first <td> or <th> in a row */
  border-left: 2px solid;
}

table thead th {
  position: sticky;
  top: 0;
  background-color: #edecec;
}
<div id="wrapper">
  <table>
    <thead>
      <tr>
        <th>A</th>
        <th>B</th>
        <th>C</th>
        <th>D</th>
        <th>E</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
        <td>1</td>
      </tr>
      <tr>
        <td>2</td>
        <td>2</td>
        <td>2</td>
        <td>2</td>
        <td>2</td>
      </tr>
      <tr>
        <td>3</td>
        <td>3</td>
        <td>3</td>
        <td>3</td>
        <td>3</td>
      </tr>
      <tr>
        <td>4</td>
        <td>4</td>
        <td>4</td>
        <td>4</td>
        <td>4</td>
      </tr>
      <tr>
        <td>5</td>
        <td>5</td>
        <td>5</td>
        <td>5</td>
        <td>5</td>
      </tr>
      <tr>
        <td>6</td>
        <td>6</td>
        <td>6</td>
        <td>6</td>
        <td>6</td>
      </tr>
      <tr>
        <td>7</td>
        <td>7</td>
        <td>7</td>
        <td>7</td>
        <td>7</td>
      </tr>
      <tr>
        <td>8</td>
        <td>8</td>
        <td>8</td>
        <td>8</td>
        <td>8</td>
      </tr>
      <tr>
        <td>9</td>
        <td>9</td>
        <td>9</td>
        <td>9</td>
        <td>9</td>
      </tr>
      <tr>
        <td>10</td>
        <td>10</td>
        <td>10</td>
        <td>10</td>
        <td>10</td>
      </tr>
    </tbody>
  </table>
</div>
waldyrious
  • 3,683
  • 4
  • 33
  • 41
blackheart
  • 3,594
  • 1
  • 12
  • 2
  • 7
    This approach solves the problem at the root and is easy to understand. I think this should be preferred over using pseudo-classes. – pj.dewitte Feb 17 '19 at 22:24
  • 12
    Yeah but when i use this there seems to a one pixel gap along the unused border sides – Sainath S.R Apr 30 '19 at 11:07
  • 1
    This solution is neat but unfortunately, there seem to be a bug in Edge that prevents it from working. – Melanie Nov 19 '19 at 20:48
  • I also can't get it to work in Edge. Does anyone have an answer for this? Thanks in advance! – JRSeabird Mar 11 '20 at 09:43
  • solution works great. only change I would make is add border-bottom to the wrapper, and add border none for last child of td. – Fiddle Freak May 29 '20 at 21:44
  • 40
    in my opinion there is no need to add such much css classes the only one thing you have to add: table { border-collapse: separate; border-spacing: 0; } – Robert Głowacki Sep 30 '20 at 17:12
  • Have same problem for Edge, wonder if any solution available? – Guizhou Feng Oct 13 '20 at 13:09
  • 4
    Unfortunately, can't use this solution for bootstrap styled tables as it makes use of `border-collapse: collapse` :/. The answer suggesting using box-shadow works though. – br3nt Oct 30 '20 at 00:03
  • 1
    Perhaps this did not work with the "old" Edge, but it works perfectly with Chromium based Edge. – dbenham Aug 18 '21 at 21:20
  • This looks weird if you have multiple rows in the header but you can fix that by removing the top border from `table th` and adding `table thead tr:first-child th { border-top: 1px solid black; }` – Jesper Sep 08 '21 at 07:38
  • Demonstrator for this answer can be found on https://jsfiddle.net/Abeeee/83cfbu7d/3/ – user1432181 Mar 10 '22 at 19:16
  • This is a nice solution for simple tables, but beware if you've got complex colspan/rowspan, `th` in multiple places, e.g. between `td`s, etc., as the styling can get messy very quickly. – tomasz86 Aug 10 '22 at 14:53
  • @br3nt I just overrode the `table` style to set separate and followed @Robert Głowacki's advice and forced border-spacing: 0. It worked with Bootstrap 5. – geerlingguy Mar 07 '23 at 21:20
66

You need to use box-shadow property instead of border-top/border-bottom. Additionally you need to delete top/bottom borders for the head and first row of table.

#wrapper {
  width: 400px;
  height: 200px;
  overflow: auto;
}

table {
  width: 100%;
  text-align: center;
  border-collapse: collapse;
}

table tr th,
table tr td {
  border: 2px solid;
}

table thead th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background-color: #edecec;
}


/* here is the trick */

table tbody:nth-of-type(1) tr:nth-of-type(1) td {
  border-top: none !important;
}

table thead th {
  border-top: none !important;
  border-bottom: none !important;
  box-shadow: inset 0 2px 0 #000000, inset 0 -2px 0 #000000;
  padding: 2px 0;
}


/* and one small fix for weird FF behavior, described in https://stackoverflow.com/questions/7517127/ */

table thead th {
  background-clip: padding-box
}
<body>
  <div id="wrapper">
    <table>
      <thead>
        <tr>
          <th>A</th>
          <th>B</th>
          <th>C</th>
          <th>D</th>
          <th>E</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>1</td>
          <td>1</td>
          <td>1</td>
          <td>1</td>
          <td>1</td>
        </tr>
        <tr>
          <td>2</td>
          <td>2</td>
          <td>2</td>
          <td>2</td>
          <td>2</td>
        </tr>
        <tr>
          <td>3</td>
          <td>3</td>
          <td>3</td>
          <td>3</td>
          <td>3</td>
        </tr>
        <tr>
          <td>4</td>
          <td>4</td>
          <td>4</td>
          <td>4</td>
          <td>4</td>
        </tr>
        <tr>
          <td>5</td>
          <td>5</td>
          <td>5</td>
          <td>5</td>
          <td>5</td>
        </tr>
        <tr>
          <td>6</td>
          <td>6</td>
          <td>6</td>
          <td>6</td>
          <td>6</td>
        </tr>
        <tr>
          <td>7</td>
          <td>7</td>
          <td>7</td>
          <td>7</td>
          <td>7</td>
        </tr>
        <tr>
          <td>8</td>
          <td>8</td>
          <td>8</td>
          <td>8</td>
          <td>8</td>
        </tr>
        <tr>
          <td>9</td>
          <td>9</td>
          <td>9</td>
          <td>9</td>
          <td>9</td>
        </tr>
      </tbody>
    </table>
  </div>
</body>
Alexander Nied
  • 12,804
  • 4
  • 25
  • 45
Andrey Radomanov
  • 1,777
  • 1
  • 13
  • 6
19
<td style="position: sticky;
           top: 0;
           left: 0;
           right: 0;
           padding: 4px 3px;
           background-color: #d3d3d3;
           box-shadow: inset 0 1px 0 black, inset 0 -1px 0 black;">
</td>

use this 100% worked

user12836853
  • 191
  • 1
  • 2
18

Because sticky positioning fluctuates between relative and fixed, the only way I can think to circumvent this out-of-box would be take advantage of psuedo classes.

I'm sure there's a more elegant manner to accomplish this but I would just alter the :after and :before psuedo classes to provide the border with absolute positioning.

#wrapper {
  width: 400px;
  height: 200px;
  overflow: auto;
}

table {
  width: 100%;
  text-align: center;
  border-collapse: collapse;
}

table tr th,
table tr td {
  border: 2px solid;
}

table thead th {
  position: -webkit-sticky;
  position: sticky;
  top: -1px;
  background-color: #edecec;
}
th:after,
th:before {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
}
th:before {
  top: 0;
  border-top: 3px solid blue;
}
th:after {
  bottom: 0;
  border-bottom: 3px solid blue;
}
<div id="wrapper">
  <table>
    <thead>
      <tr>
        <th>A</th>
        <th>B</th>
        <th>C</th>
        <th>D</th>
        <th>E</th>
      </tr>
    </thead>
    <tr>
      <td>1</td>
      <td>1</td>
      <td>1</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <td>2</td>
      <td>2</td>
      <td>2</td>
      <td>2</td>
      <td>2</td>
    </tr>
    <tr>
      <td>3</td>
      <td>3</td>
      <td>3</td>
      <td>3</td>
      <td>3</td>
    </tr>
    <tr>
      <td>4</td>
      <td>4</td>
      <td>4</td>
      <td>4</td>
      <td>4</td>
    </tr>
    <tr>
      <td>5</td>
      <td>5</td>
      <td>5</td>
      <td>5</td>
      <td>5</td>
    </tr>
    <tr>
      <td>6</td>
      <td>6</td>
      <td>6</td>
      <td>6</td>
      <td>6</td>
    </tr>
    <tr>
      <td>7</td>
      <td>7</td>
      <td>7</td>
      <td>7</td>
      <td>7</td>
    </tr>
    <tr>
      <td>8</td>
      <td>8</td>
      <td>8</td>
      <td>8</td>
      <td>8</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tr>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
      <td>9</td>
    </tr>
    <tbody>
    </tbody>
  </table>
</div>
soulshined
  • 9,612
  • 5
  • 44
  • 79
  • Thank you for updated answer. But still doesn't work for left and right, isn't ? – Cataclysm May 16 '18 at 04:21
  • See edit @Cataclysm and let me know your thoughts. I just put the table border back, or do you want them to be of a certain color separate of table border color? – soulshined May 16 '18 at 04:26
  • Thank you ! Now I'm almost done with your idea .Here is [updated fiddle](https://jsfiddle.net/uufafj27/5/) – Cataclysm May 16 '18 at 04:52
7

How about replacing border with outline? I dare to say that especially for table elements the outline acts almost the same as border. I know it is not out of the box model etc. but visually is identical here. Try this:

table tr th,
table tr td {
  border: 0; 
  outline: 2px solid;
}

Here's a live snippet demonstrating this approach:

#wrapper {
  width: 100%;
  height: 150px;
  overflow: auto;
}
table {
  width: 100%;
  text-align: center;
}
table thead {
  position: sticky;
  top: 2px; /* Must match the outline width */
  background-color: #edecec;
}
table tr th,
table tr td {
  border: 0; 
  outline: 2px solid;
}
<div id="wrapper">
  <table>
    <thead>
      <tr><th>A</th><th>B</th><th>C</th><th>D</th></tr>
    </thead>
    <tbody>
      <tr><td>0</td><td>0</td><td>0</td><td>0</td></tr>
      <tr><td>1</td><td>1</td><td>1</td><td>1</td></tr>
      <tr><td>2</td><td>2</td><td>2</td><td>2</td></tr>
      <tr><td>3</td><td>3</td><td>3</td><td>3</td></tr>
      <tr><td>4</td><td>4</td><td>4</td><td>4</td></tr>
      <tr><td>5</td><td>5</td><td>5</td><td>5</td></tr>
      <tr><td>6</td><td>6</td><td>6</td><td>6</td></tr>
      <tr><td>7</td><td>7</td><td>7</td><td>7</td></tr>
      <tr><td>8</td><td>8</td><td>8</td><td>8</td></tr>
      <tr><td>9</td><td>9</td><td>9</td><td>9</td></tr>
    </tbody>
  </table>
</div>

Bear in mind, outline is always rendered around the whole element, there is nothing like outline-left, so it is not silver bullet. See outline on MDN for further details.

UPDATE: Well I overlooked one detail, the outline is not properly aligned with tbody borders. You have to eiter swap border and outline for the whole table or use transform and nudge the thead, but perhaps it is not good way to go (it depends, of course).

waldyrious
  • 3,683
  • 4
  • 33
  • 41
Kout
  • 444
  • 1
  • 7
  • 14
  • 1
    I was going to post this too then found yours. This only works properly in *very* specific circumstances -- or if you can intimidate it into looking nice -- but when you're in those circumstances, it's a fantastic solution. – Jason C Jun 06 '22 at 16:00
3

Use ::after pseudo selector to emulate, lets say, right border

Example:

th::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  border-right: 1px solid #e7e7e7;
}


Ayan
  • 8,192
  • 4
  • 46
  • 51
2

Now I can set the border styles with psuedo classes as user @soulshined suggested. Belows are css changes to work and here is JSFiddle Link. Already tested on chrome and firefox.

#wrapper {
  width: 400px;
  height: 200px;
  overflow: auto;
  padding: 1px;
}

table {
  width: 100%;
  text-align: center;
  border-collapse: collapse;
}
table tr th {
  border: 1px solid green;
}
table tr th:first-child {
  border-left: 1px solid green;
}
table tr td {
  border: 1px solid green;
}

table thead th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  background-color: #edecec;
}

th::before {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
  height: 100%;
  border-right: 1px solid green;
  display: block;
  top : 1px;
}

th::after {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
  height: 100%;
  border-bottom: 1px solid green;
  border-top: 1px solid green;
  display: block;
  top : -1px;
}
Cataclysm
  • 7,592
  • 21
  • 74
  • 123
1

Tried some of the existing answers, but as comments have mentioned, they result in a one pixel gap between the borders.

Made a workaround, though it requires JavaScript. It places a <div> in each <th>, on resize it fits the <div> to the <th>. The <th> border is then instead applied to the <div>.

Note that as of writing this, position: sticky is not working in Chromium on <thead>, it works in Firefox. However, the position: sticky related CSS does not affect the page, if the JavaScript is not executed.

Click Run code snippet to see if it works, in your browser.

function onResize() {
    $("thead th div").each(function () {
        var width = $(this).parent().outerWidth() - 2; // -2 * border-width
        $(this).css("width", width + "px");
    });
}

$("thead th").each(function () {
    $(this).append($("<div>"));
});

$(window).on("resize", onResize);
onResize();
table {
    width: 100%;
    border-collapse: collapse;
}

th {
    text-align: left;
}

th, td {
    padding: 12px 20px;

    border: 1px solid rgba(0, 0, 0, 0.125);
}

table thead {
    position: sticky;
    top: 0;

    background: #FFF;
}

/* Used as a workaround for position: sticky not rendering the border */
thead th div {
    width: 30px;
    margin: 0 -20px; /* -20px to negate the 20px from the padding */
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: -1;

    border-right: 1px solid rgba(0, 0, 0, 0.125);
    border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>

<table>
    <thead>
        <tr>
            <th>A</th>
            <th>B</th>
            <th>C</th>
            <th>D</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>
    </tbody>
</table>

Wrap the previous JavaScript in the following, to limit it to Firefox.

if (navigator.userAgent.toLowerCase().indexOf("firefox") > -1) {
    // ...
}

Screenshot (Firefox 71):

Screenshot (Firefox 71)

vallentin
  • 23,478
  • 6
  • 59
  • 81
1

For those seeking a solution that produces 1px borders, the following tweak to @blackheart's answer worked for me:

/* Styles to make the StackOverflow snippet render nicely */
#wrapper {
  width: 100%;
  height: 150px;
  overflow: auto;
}
table {
  width: 100%;
  text-align: center;
}

/* Actual sticky-header styles */
table {
  border-spacing: 0;
}
table thead {
  position: sticky;
  top: 0;
  background-color: #edecec;
}
table th { 
  border: 1px solid;
  border-left: none;
}
table td {
  border: 1px solid;
  border-left: none;
  border-top: none;
}
table tr > th:first-child,
table tr > td:first-child {
  border-left: 1px solid;
}
<div id="wrapper">
  <table>
    <thead>
      <tr><th>A</th><th>B</th><th>C</th><th>D</th></tr>
    </thead>
    <tbody>
      <tr><td>0</td><td>0</td><td>0</td><td>0</td></tr>
      <tr><td>1</td><td>1</td><td>1</td><td>1</td></tr>
      <tr><td>2</td><td>2</td><td>2</td><td>2</td></tr>
      <tr><td>3</td><td>3</td><td>3</td><td>3</td></tr>
      <tr><td>4</td><td>4</td><td>4</td><td>4</td></tr>
      <tr><td>5</td><td>5</td><td>5</td><td>5</td></tr>
      <tr><td>6</td><td>6</td><td>6</td><td>6</td></tr>
      <tr><td>7</td><td>7</td><td>7</td><td>7</td></tr>
      <tr><td>8</td><td>8</td><td>8</td><td>8</td></tr>
      <tr><td>9</td><td>9</td><td>9</td><td>9</td></tr>
    </tbody>
  </table>
</div>

Note: I should point out that @Kout's solution of using outline instead of border is also ingenious and much shorter (requiring only one additional ruleset instead of 3), but unfortunately the 1px version (where I attempted to merge adjacent outlines using a negative outline-offset) works in Firefox but not in Chrome, where I see 1px outline around the <table> and <th>s, but still see 2px outlines between the <td>s:

/* Styles to make the StackOverflow snippet render nicely */
#wrapper {
  width: 100%;
  height: 150px;
  overflow: auto;
}
table {
  width: 100%;
  text-align: center;
}

/* Actual sticky-header styles */
table {
  border-spacing: 0;
  /* Subtract a pixel because the width calculation
   * doesn't account for outlines */
  width: calc(100% - 1px);
}
table thead {
  position: sticky;
  top: 0;
  background-color: #edecec;
}
table tr th,
table tr td {
  outline: 0.5px solid;
  outline-offset: -0.5px;
}
<div id="wrapper">
  <table>
    <thead>
      <tr><th>A</th><th>B</th><th>C</th><th>D</th></tr>
    </thead>
    <tbody>
      <tr><td>0</td><td>0</td><td>0</td><td>0</td></tr>
      <tr><td>1</td><td>1</td><td>1</td><td>1</td></tr>
      <tr><td>2</td><td>2</td><td>2</td><td>2</td></tr>
      <tr><td>3</td><td>3</td><td>3</td><td>3</td></tr>
      <tr><td>4</td><td>4</td><td>4</td><td>4</td></tr>
      <tr><td>5</td><td>5</td><td>5</td><td>5</td></tr>
      <tr><td>6</td><td>6</td><td>6</td><td>6</td></tr>
      <tr><td>7</td><td>7</td><td>7</td><td>7</td></tr>
      <tr><td>8</td><td>8</td><td>8</td><td>8</td></tr>
      <tr><td>9</td><td>9</td><td>9</td><td>9</td></tr>
    </tbody>
  </table>
</div>
waldyrious
  • 3,683
  • 4
  • 33
  • 41
0

This is, I believe the simplest answer.

The accepted answer don't provide a solution for a 1px border.

Note: this is a work around, so don't gripe me.

If you remove the border-top from the table header, the table has nothing to collapse, so no space is created to see through to the underlying cells. Replace the border-top with a border of the same style in a holding DIV.

css:

table thead tr:nth-child(1) th {
  position: sticky; 
  border-top:0;
}

.table_holder {
  border-top:1px solid black;
}

html:

<div id="table_holder">
  <table> ... </table>
</div>
ChrisAdmin
  • 982
  • 1
  • 12
  • 32
0
  1. sticky with header border visible

html{
    padding: 0px;
    margin: 0px;
}
body{
    padding: 0px;
    margin: 0px;
    width:100%;
}

 th,td{
     padding:10px;
     border: 0.1px solid #e8e0e0;
     padding-right:10px;
 }   
 th{
     text-align: center;
    background: #f3f3f3;
    background-clip: padding-box;
 }


thead th:first-child {
  left: 0;
  z-index: 1;
}
tbody th:first-child  {
   text-align: center;
  position: -webkit-sticky; /* for Safari */
  position: sticky;
  left: 0;
 
}
tbody th,thead th:first-child  {
/* display:flex; */
/* text-align: center;
align-items: center;
align-self: center; */
    width:30px;
    
    min-width: 30px;
    max-width: 30px;
    word-break: break-all;
}
.fixed_header{
    width: 100%;
    /* height: 500px; */
    table-layout: fixed;
    border-collapse: collapse;
}
/* .fixed_header tbody{
  overflow: auto;
 
} */

/* fixed header */
thead th {
    /* for Safari */
 text-align: center;
  position: -webkit-sticky;
   position: sticky;

  top: 0;


}

.fixed_header th, .fixed_header td {
    padding:10px;
  /* text-align: left; */
  width: 90px;
}

.fixed_header td{
    /* padding:10px; */
/* word-break: break-all; */
/* max-width: 200px; */
}
.table_container{
/* position: fixed; */
position: relative;
width:100% ;
min-height: 500px;
overflow: auto;
background:cornsilk;
}
 <table class="fixed_header">
                <thead>
                  <tr>
                   <th></th>
                    <th>Col 1</th>
                    <th>Col 2</th>
                    <th>Col 3</th>
                    <th>Col 4</th>
                    <th>Col 5</th>
                          <th>Col 1</th>
                    <th>Col 2</th>
                    <th>Col 3</th>
                    <th>Col 4</th>
                    <th>Col 5</th>
                          <th>Col 1</th>
                    <th>Col 2</th>
                    <th>Col 3</th>
                    <th>Col 4</th>
                    <th>Col 5</th>
                          <th>Col 1</th>
                    <th>Col 2</th>
                    <th>Col 3</th>
                    <th>Col 4</th>
                    <th>Col 5</th>
                  </tr>
                </thead>
                <tbody>
                  <tr>
                    <th>1</th>
                    <td>row 1-1</td>
                    <td>row 1-2</td>
                    <td>row 1-3</td>
                    <td>row 1-4</td>
                    <td>row 1-1</td>
                    <td>row 1-2</td>
                    <td>row 1-3</td>
                    <td>row 1-4</td>
                    <td>row 1-1</td>
                    <td>row 1-2</td>
                    <td>row 1-3</td>
                    <td>row 1-4</td>
                    <td>row 1-1</td>
                    <td>row 1-2</td>
                    <td>row 1-3</td>
                    <td>row 1-4</td>
                    <td>row 1-1</td>
                    <td>row 1-2</td>
                    <td>row 1-3</td>
                    <td>row 1-4</td>  
                </tr>     

                </tbody>
              </table>
            </div>
Balaji
  • 9,657
  • 5
  • 47
  • 47
0

If you use postion: sticky, The td's border is inherit from tr, If you only use border-collapse: separate, This will produce at least 2px border in your table ,so you should set every td's border-right and border-bottom;

Here is the code:

<style>
table {
 border-collapse: separate;
}

table th:first-child,
table td:first-child{
  border-left: 1px solid;
}

table th {
  border-top: 1px solid;
}

table th,td {
  text-align: center;
}

th,td,tr {
  border-bottom: 1px solid;
  border-right: 1px solid;
  text-align: center;
  height: 40px;
  line-height: 40px;
}

</style>
M.H
  • 29
  • 1
-3
<style>
thead{
    position: sticky;
    z-index: 2;
    top: 0;
    background-color: white;
};
    
table {
    border-collapse: separate;
}
</style>
<table cellspacing="0">
    <!--...-->
</table>
  • 3
    Code without any explanation are rarely helpful. Stack Overflow is about learning, not providing snippets to blindly copy and paste. Please edit your question and explain how it answers the specific question being asked. See [How to Answer](https://stackoverflow.com/help/how-to-answer). – Sfili_81 Mar 17 '22 at 15:33
-4

Use table thead th instead of table thead. This is demo. https://jsfiddle.net/nhatphamcdn/9dhqchtu/

V.Nhat
  • 44
  • 3