9

I am using a data table for which i am using the traditional table markup but what i want is my table start from somewhere in the page and i want my table header to be fixed where it starts and table body should scroll by taking the remaining page height
i am showing a small example here

https://codepen.io/avreddy/pen/eyYBdB?editors=1100

.datatable{
  width: 50%;
  border-collapse: collapse;
}
td{
  text-align: center;
  border: 1px solid;
  padding:5px;
}
<body>
  <div class="somecontent">
    <h4>Some Content</h4><h4>Some Content</h4><h4>Some Content</h4>
  </div>
  <table class="datatable">
    <thead>
      <tr>
      <th>col1</th>
      <th>col2</th>
      <th>col3</th>
      </tr>
    </thead>
    <tbody>
      <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr>
      <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr>
    </tbody>
  </table>
</body>


I know we can get it from flex-box responsive table but my limitation is i have to use traditional table markup so if tell me if there is any other way than flexbox responsive table
Thank you

Salman A
  • 262,204
  • 82
  • 430
  • 521
Aravind Reddy
  • 747
  • 6
  • 20

3 Answers3

7

.datatable{
  width: 50%;
  border-collapse: collapse;
}

/*this code is added to fix header at the top of the screen*/

thead th{
  position: sticky; /*stick the element*/
  top: 0px;
  background: grey;
}
td{
  text-align: center;
  border: 1px solid;
  padding:5px;
}
<body>
  <div class="somecontent">
    <h4>Some Content</h4><h4>Some Content</h4><h4>Some Content</h4>
  </div>
  <table class="datatable">
    <thead>
      <tr>
      <th>col1</th>
      <th>col2</th>
      <th>col3</th>
      </tr>
    </thead>
    <tbody>
      <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr>
      <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr><tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
        </tr>
    </tbody>
  </table>
</body>

you want something like this header will be sticky on top of the page.

Prateik Darji
  • 2,297
  • 1
  • 13
  • 29
7

Scrollable table + fixed header tricks cannot work with automatic column widths. This is because they change the display property of tbody element which creates a new (anonymous) table having different column widths.

The best solution I could think of is to create a copy of the table (so that both have identical content and therefore identical column widths) and manipulate them independently.

In the following example, I use jQuery to create a copy of table and visibility: collapse to hide the table header and body:

$(function() {
  $("#button-1").one("click", function() {
    $(".datatable").clone().insertAfter(".datatable");
    $(".datatable").eq(0).addClass("tablehead");
    $(".datatable").eq(1).addClass("tablebody");
  });
});
/* full height */
html { height: 100%; }
body { height: 100%; margin: 0; padding: 0; font: medium monospace; }

/* make last item scrollable and occupy available height */
#wrapper { height: 100%; display: flex; flex-direction: column; overflow-y: hidden; }
#wrapper > :last-child { display: block; flex: 1; overflow-y: auto; }

/* make thead and tbody collapse when JavaScript adds the classes */
.datatable.tablehead tbody { visibility: collapse; }
.datatable.tablebody thead { visibility: collapse; }

/* beautify */
.datatable { border-collapse: collapse; }
.datatable th { border: 1px solid; background-color: #CCC; }
.datatable td { border: 1px solid; padding: 5px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<!--
Notes:

1) Using a wrapper div instead of body because third party scripts
often append elements to body element breaking our flex model

2) Width of second column is determined by thead > 1st row > 2nd column
while width of third column is determined by tbody > 1st row > 3rd column

3) If JavaScript fails the result is still usable
-->

<div id="wrapper">
  <div>
    <h4>Some Content</h4>
    <h4>Some Content</h4>
    <p><button type="button" id="button-1">Convert to fixed header</button></p>
  </div>
  <table class="datatable">
    <thead>
      <tr>
        <th>col 1</th>
        <th>col 2 (wide header)</th>
        <th>col 3</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3 (wide content)</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
      <tr>
        <td>1</td>
        <td>2</td>
        <td>3</td>
      </tr>
    </tbody>
  </table>
</div>

Note that visibility: collapse is not implemented properly in all browsers. Alternate solution is to wrap the two tables inside divs: fixed height div for header table, scrollable div for body table. But then you need to set the height of the header.

Salman A
  • 262,204
  • 82
  • 430
  • 521
  • you cannot use the flex display to datatable because it will disturb the table layout you can see the children of table are not aligned according to the table header due to that flex display only – Aravind Reddy Dec 19 '17 at 14:06
  • @AravindReddy yes this is a known problem. The workaround is to specify a fixed % or px width on each column. – Salman A Dec 19 '17 at 14:33
  • @AravindReddy see revised answer. – Salman A Dec 19 '17 at 22:34
2

Try position: sticky for your thead.

You need to apply it to the th to work in all browsers.

thead th {
    position: sticky;           // Sticky makes it, well, sticky
    top: 0px;                   // At which offset it should become sticky
    background-color: #fff;     // Otherwise the th is transparent
}

Codepen


To make it sticky from it's current position, you can use a little javascript:

let topOffset = document.getElementById("datatable").offsetTop;
let elementList = document.querySelectorAll('.datatable thead th');

for (i = 0; i < elementList.length; i++) {
  elementList[i].style.top = topOffset + 'px';
}

It may look a little weird now in the pen (overlap/transparency), but when you say there is content above the thead (a header, an alertbox, ..), just a apply a zIndex higher than the table, and it should look fine.

I'm currently working on a website with a fullscreen table, a sticky header, and a variable height content block above it, and this is what I use (although the offset is calculated a little different with AngularJS).

Codepen

Jeffrey Roosendaal
  • 6,872
  • 8
  • 37
  • 55
  • i know this could work if you know the top offset but my question is i dont how much space the top content gonna use i want the thead stick where it starts – Aravind Reddy Dec 19 '17 at 13:03
  • Yeah bro i know that too and i have tried too, but the thing is table loads first and the top content loads later because i use different controllers for both so if i use the java code which you mentioned it will take the top offset before he header top content loads hence it is useless – Aravind Reddy Dec 19 '17 at 13:41
  • Wrap it in a function block then, and invoke it with onload: `
    `, or invoke it in your controller once it loads.
    – Jeffrey Roosendaal Dec 19 '17 at 13:44
  • ok i'll try but see the other answers i think one have answered my question without javascript – Aravind Reddy Dec 19 '17 at 13:46