Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML table with fixed (frozen) columns and headers

I've been searching over the web for the way to make a table with fixed (frozen) columns and header. Seems like I finally found the solution and modified it to my needs.

There original fiddle is here.

Here is my modified solution. I tested it in Chrome (version: 55.0.2883.87 m) and Firefox (version: 51.0.1).

The problem is that it works not completely in IE (version: 11.0.9600.18427). During the horizontal scrolling a frozen part of the header is getting scrolled too. Could someone help me to make it working in IE? And one more question: is the approach safe to use? I mean if it's using some unspecified behavior, then some of the future browsers or even some of the modern browsers might display my table in a wrong way, and it's better to use a safe solution with a few different tables and synchronizing scroll position and rows height. UPD: one more question: how to make this work stable on the mobile devices?

Here is some code that demonstrates the approach:

$(document).ready(function() {    $('tbody').scroll(function(e) { //detect a scroll event on the tbody    	/*      Setting the thead left value to the negative valule of tbody.scrollLeft will make it track the movement      of the tbody element. Setting an elements left value to that of the tbody.scrollLeft left makes it maintain 			it's relative position at the left of the table.          */      $('thead').css("left", -$("tbody").scrollLeft()); //fix the thead relative to the body scrolling      $('thead th:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first cell of the header      $('tbody td:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first column of tdbody    });  });
.container {    height:200px;     width:400px;    overflow: hidden;  }    table {    position: relative;    background-color: #aaa;    border-collapse: collapse;  table-layout: fixed;  display: flex;  flex-direction: column;  height: 100%;  width: 100%;  }      /*thead*/  thead {    position: relative;    display: block; /*seperates the header from the body allowing it to be positioned*/  }    thead th {    background-color: #99a;    min-width: 120px;    border: 1px solid #222;  }    thead th:nth-child(1) {/*first cell in the header*/    position: relative;    background-color: #88b;  }      /*tbody*/  tbody {    flex: 1;    position: relative;    display: block; /*seperates the tbody from the header*/    overflow: auto;  }    tbody td {    background-color: #bbc;    min-width: 120px;    border: 1px solid #222;  }    tbody tr td:nth-child(1) {  /*the first cell in each tr*/    position: relative;    background-color: #99a;  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <body>  <div class="container">      <table>      <thead>        <tr>          <th>Name<br/>123</th>          <th>Town</th>          <th>County</th>          <th>Age</th>          <th>Profession</th>          <th>Anual Income</th>          <th>Matital Status</th>          <th>Children</th>        </tr>         <tr>          <th>Name</th>          <th>Town</th>          <th>County</th>          <th>Age<br/>123<br/>321</th>          <th>Profession</th>          <th>Anual Income</th>          <th>Matital Status</th>          <th>Children</th>        </tr>      </thead>      <tbody>        <tr>          <td>John Smith</td>          <td>Macelsfield</td>          <td>Cheshire<br/>123</td>          <td>52</td>          <td>Brewer</td>          <td>£47,000</td>          <td>Married</td>          <td>2</td>        </tr>        <tr>          <td>Jenny Jones<br/>123<br/>312</td>          <td>Threlkeld</td>          <td>Cumbria</td>          <td>34</td>          <td>Shepherdess</td>          <td>£28,000</td>          <td>Single</td>          <td>0</td>        </tr>        <tr>          <td>Peter Frampton</td>          <td>Avebury</td>          <td>Wiltshire</td>          <td>57</td>          <td>Musician</td>          <td>£124,000</td>          <td>Married</td>          <td>4</td>        </tr>        <tr>          <td>Simon King</td>          <td>Malvern</td>          <td>Worchestershire</td>          <td>48</td>          <td>Naturalist</td>          <td>£65,000</td>          <td>Married</td>          <td>2</td>        </tr>        <tr>          <td>Lucy Diamond</td>          <td>St Albans</td>          <td>Hertfordshire</td>          <td>67</td>          <td>Pharmasist</td>          <td>Retired</td>          <td>Married</td>          <td>3</td>        </tr>        <tr>          <td>Austin Stevenson</td>          <td>Edinburgh</td>          <td>Lothian </td>          <td>36</td>          <td>Vigilante</td>          <td>£86,000</td>          <td>Single</td>          <td>Unknown</td>        </tr>        <tr>          <td>Wilma Rubble</td>          <td>Bedford</td>          <td>Bedfordshire</td>          <td>43</td>          <td>Housewife</td>          <td>N/A</td>          <td>Married</td>          <td>1</td>        </tr>        <tr>          <td>Kat Dibble</td>          <td>Manhattan</td>          <td>New York</td>          <td>55</td>          <td>Policewoman</td>          <td>$36,000</td>          <td>Single</td>          <td>1</td>        </tr>        <tr>          <td>Henry Bolingbroke</td>          <td>Bolingbroke</td>          <td>Lincolnshire</td>          <td>45</td>          <td>Landowner</td>          <td>Lots</td>          <td>Married</td>          <td>6</td>        </tr>        <tr>          <td>Alan Brisingamen</td>          <td>Alderley</td>          <td>Cheshire</td>          <td>352</td>          <td>Arcanist</td>          <td>A pile of gems</td>          <td>Single</td>          <td>0</td>        </tr>      </tbody>    </table>      </div>  </body>
like image 223
Artem Kachanovskyi Avatar asked Feb 02 '17 14:02

Artem Kachanovskyi


People also ask

How do I freeze column headers in HTML?

To freeze the row/column we can use a simple HTML table and CSS. HTML: In HTML we can define the header row by <th> tag or we can use <td> tag also. Below example is using the <th> tag. We also put the table in DIV element to see the horizontal and vertical scrollbar by setting the overflow property of the DIV element.


1 Answers

This is very peculiar. It appears that the problematic code is this line:

$('thead').css("left", -$("tbody").scrollLeft()); //fix the thead relative to the body scrolling 

It looks like IE11 handles relative positioning of nested elements differently (than Chrome and other browsers). In this case, you are positioning thead with relative positioning with an offset. You are also positioning thead th (it's children) with an offset and relative positioning. Chrome appears to be positioning thead relative to table, and then positioning th relative to thead. IE11, on the other hand, appears to be positioning thead relative to table, and then th just inherits that same positioning regardless of its own positioning.

A fix for this would be the following: on IE11, handle the positioning differently for thead. Instead of setting the positioning on the parent thead, set the positioning on the thead th elements. In that way, your first column will not be 'forced' to inherit thead's positioning (in IE).

$(document).ready(function() {   var isIE11 = !!navigator.userAgent.match(/Trident.*rv\:11\./);   var customScroller;   if (isIE11)     customScroller = function() {       $('thead th').css("left", -$("tbody").scrollLeft()); //if using IE11, fix the th element      };   else     customScroller = function() {       $('thead').css("left", -$("tbody").scrollLeft()); //if not using IE11, fix the thead element     };    $('tbody').scroll(function(e) { //detect a scroll event on the tbody     /*     Setting the thead left value to the negative valule of tbody.scrollLeft will make it track the movement     of the tbody element. Setting an elements left value to that of the tbody.scrollLeft left makes it maintain             it's relative position at the left of the table.         */     customScroller(); //fix the thead relative to the body scrolling     $('thead th:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first cell of the header     $('tbody td:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first column of tdbody   }); }); 

Here is a full example with your code, showing different handlings based on the browser:

https://jsfiddle.net/8tx4xfhx/5/

Alsol, it would be nice to see if anyone has noticed this behavior before. It appears that IE11 handles nested relative positioning differently than other browsers. Is there a spec somewhere that defines what the standard should be? Should relative positioning be inherited like IE does it? Or should relative positioning always be relative to the parent? I would think the latter. But performance considerations must also be taken.

like image 112
Matt Spinks Avatar answered Oct 14 '22 06:10

Matt Spinks