Border style do not work with sticky position element

HtmlCss

Html Problem Overview


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.

Html Solutions


Solution 1 - Html

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: 400px;
  height: 200px;
  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;
}

Solution 2 - Html

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>

Solution 3 - Html

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>

Solution 4 - Html

<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

Solution 5 - Html

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;
}

Solution 6 - Html

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;
}

Solution 7 - Html

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.

<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>

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);
}

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();

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)

Solution 8 - Html

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>

Solution 9 - Html

  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>

Solution 10 - Html

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;
}

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).

Solution 11 - Html

<style>
thead{
    position: sticky;
    z-index: 2;
    top: 0;
    background-color: white;
};
    
table {
    border-collapse: separate;
}
</style>
<table cellspacing="0">
    <!--...-->
</table>

Solution 12 - Html

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCataclysmView Question on Stackoverflow
Solution 1 - HtmlblackheartView Answer on Stackoverflow
Solution 2 - HtmlAndrey RadomanovView Answer on Stackoverflow
Solution 3 - HtmlsoulshinedView Answer on Stackoverflow
Solution 4 - Htmluser12836853View Answer on Stackoverflow
Solution 5 - HtmlAyanView Answer on Stackoverflow
Solution 6 - HtmlCataclysmView Answer on Stackoverflow
Solution 7 - HtmlvallentinView Answer on Stackoverflow
Solution 8 - HtmlChrisAdminView Answer on Stackoverflow
Solution 9 - HtmlßãlãjîView Answer on Stackoverflow
Solution 10 - HtmlKoutView Answer on Stackoverflow
Solution 11 - Htmlvan'GelionView Answer on Stackoverflow
Solution 12 - HtmlV.NhatView Answer on Stackoverflow