Wednesday, February 22, 2017

HTML table with fixed (frozen) columns and headers

Leave a Comment

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>

3 Answers

Answers 1

You should try below code sample with reference of jquery.floatThead.js.

                    var $demoTable = $("div.table-responsive table");                     $demoTable.floatThead({                         top: 200,                         scrollContainer: function ($table) {                                                             return $table.closest('.table-responsive');                         },                         position: 'absolute'                     }); 

you need to get the reference of jquery.floatThead.js file and try to apply this on table.

You can check this working on below link. http://mkoryak.github.io/floatThead/

Answers 2

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.

Answers 3

Generally for frozen rows & columns, I always prefer to use a css-only solution for best browser-compatibility.

I have tried to replicate your code here with a css-only solution.

I am working on a mac, so don't have access to IE. Please verify if its working fine on the same.

Updated fiddle: https://jsfiddle.net/nashcheez/bzuasLcz/81/

Refer code:

table {    position: relative;    width: 700px;    background-color: #aaa;    overflow: hidden;    border-collapse: collapse;  }  /*thead*/    thead {    position: relative;    display: block;    /*seperates the header from the body allowing it to be positioned*/    width: 700px;    overflow: visible;  }  thead th {    background-color: #99a;    min-width: 120px;    height: 36px;    min-height: 36px;    border: 1px solid #222;  }  thead th:nth-child(1) {    /*first cell in the header*/    position: relative;    display: block;    background-color: #88b;  }  tbody tr td:nth-child(2) {    margin-left: 124px;    display: block;  }  /*tbody*/    tbody {    display: block;    width: 700px;    height: 239px;    overflow-y: auto;  }  tbody td {    background-color: #bbc;    min-width: 120px;    border: 1px solid #222;    height: 36px;    min-height: 36px;  }  tbody tr td:nth-child(1) {    /*the first cell in each tr*/    position: absolute;    display: inline-block;    background-color: #99a;  }
<body>    <table>      <thead>        <tr>          <th>Name</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>      </thead>      <tbody>        <tr>          <td>John Smith</td>          <td>Macelsfield</td>          <td>Cheshire</td>          <td>52</td>          <td>Brewer</td>          <td>£47,000</td>          <td>Married</td>          <td>2</td>        </tr>        <tr>          <td>Jenny Jones</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>  </body>

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment