Table header to stay fixed at the top when user scrolls it out of view with jQuery

JqueryHtmlHtml Table

Jquery Problem Overview


I am trying to design an HTML table where the header will stay at the top of the page when AND ONLY when the user scrolls it out of view. For example, the table may be 500 pixels down from the page, how do I make it so that if the user scrolls the header out of view (browser detects its no longer in the windows view somehow), it will stay put at the top? Anyone can give me a Javascript solution to this?

<table>
  <thead>
    <tr>
      <th>Col1</th>
      <th>Col2</th>
      <th>Col3</th>
    </tr>
  </thead>
  <tbody>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
  </tbody>
</table>

So in the above example, I want the <thead> to scroll with the page if it goes out of view.

IMPORTANT: I am NOT looking for a solution where the <tbody> will have a scrollbar (overflow:auto).

Jquery Solutions


Solution 1 - Jquery

You would do something like this by tapping into the scroll event handler on window, and using another table with a fixed position to show the header at the top of the page.

HTML:

<table id="header-fixed"></table>

CSS:

#header-fixed {
    position: fixed;
    top: 0px; display:none;
    background-color:white;
}

JavaScript:

var tableOffset = $("#table-1").offset().top;
var $header = $("#table-1 > thead").clone();
var $fixedHeader = $("#header-fixed").append($header);

$(window).bind("scroll", function() {
    var offset = $(this).scrollTop();
    
    if (offset >= tableOffset && $fixedHeader.is(":hidden")) {
        $fixedHeader.show();
    }
    else if (offset < tableOffset) {
        $fixedHeader.hide();
    }
});

This will show the table head when the user scrolls down far enough to hide the original table head. It will hide again when the user has scrolled the page up far enough again.

Working example: http://jsfiddle.net/andrewwhitaker/fj8wM/

Solution 2 - Jquery

Pure CSS (without IE11 support):

table th {
    position: -webkit-sticky; // this is for all Safari (Desktop & iOS), not for Chrome
    position: sticky;
    top: 0;
    z-index: 1; // any positive value, layer order is global
    background: #fff; // any bg-color to overlap
}

Solution 3 - Jquery

Well, I was trying to obtain the same effect without resorting to fixed size columns or having a fixed height for the entire table.

The solution I came up with is a hack. It consists of duplicating the entire table then hiding everything but the header, and making that have a fixed position.

HTML
<div id="table-container">
<table id="maintable">
    <thead>
        <tr>
            <th>Col1</th>
            <th>Col2</th>
            <th>Col3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>some really long line here instead</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
                <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
                <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
    </tbody>
</table>
<div id="bottom_anchor"></div>
</div>
CSS
body { height: 1000px; }
thead{
    background-color:white;
}
javascript
function moveScroll(){
    var scroll = $(window).scrollTop();
    var anchor_top = $("#maintable").offset().top;
    var anchor_bottom = $("#bottom_anchor").offset().top;
    if (scroll>anchor_top && scroll<anchor_bottom) {
    clone_table = $("#clone");
    if(clone_table.length == 0){
        clone_table = $("#maintable").clone();
        clone_table.attr('id', 'clone');
        clone_table.css({position:'fixed',
                 'pointer-events': 'none',
                 top:0});
        clone_table.width($("#maintable").width());
        $("#table-container").append(clone_table);
        $("#clone").css({visibility:'hidden'});
        $("#clone thead").css({'visibility':'visible','pointer-events':'auto'});
    }
    } else {
    $("#clone").remove();
    }
}
$(window).scroll(moveScroll); 

See here: http://jsfiddle.net/QHQGF/7/

Edit: updated the code so that the thead can receive pointer events(so buttons and links in the header still work). This fixes the problem reported by luhfluh and Joe M.

New jsfiddle here: http://jsfiddle.net/cjKEx/

Solution 4 - Jquery

I wrote a plugin that does this. Ive been working on it for about a year now and I think it handles all the corner cases pretty well:

  • scrolling within a container with overflow
  • scrolling within a window
  • taking care of what happens when you resize the window
  • keeping your events bound to the header
  • most importantly it doesn't force you to change your table's css to make it work

Here are some demos/docs:
http://mkoryak.github.io/floatThead/

Solution 5 - Jquery

I was able to fix the problem with changing column widths. I started with Andrew's solution above (thanks so much!) and then added one little loop to set the widths of the cloned td's:

$("#header-fixed td").each(function(index){
	var index2 = index;
	$(this).width(function(index2){
	    return $("#table-1 td").eq(index).width();
	});
});

This solves the problem without having to clone the entire table and hide the body. I'm brand new to JavaScript and jQuery (and to stack overflow), so any comments are appreciated.

Solution 6 - Jquery

This is by far the best solution I've found for having a fixed table header.

UPDATE 5/11: Fixed horizontal scrolling bug as pointed out by Kerry Johnson

Codepen: https://codepen.io/josephting/pen/demELL

;(function($) {
   $.fn.fixMe = function() {
      return this.each(function() {
         var $this = $(this),
            $t_fixed;
         function init() {
            $this.wrap('<div class="container" />');
            $t_fixed = $this.clone();
            $t_fixed.find("tbody").remove().end().addClass("fixed").insertBefore($this);
            resizeFixed();
         }
         function resizeFixed() {
           $t_fixed.width($this.outerWidth());
            $t_fixed.find("th").each(function(index) {
               $(this).css("width",$this.find("th").eq(index).outerWidth()+"px");
            });
         }
         function scrollFixed() {
            var offsetY = $(this).scrollTop(),
            offsetX = $(this).scrollLeft(),
            tableOffsetTop = $this.offset().top,
            tableOffsetBottom = tableOffsetTop + $this.height() - $this.find("thead").height(),
            tableOffsetLeft = $this.offset().left;
            if(offsetY < tableOffsetTop || offsetY > tableOffsetBottom)
               $t_fixed.hide();
            else if(offsetY >= tableOffsetTop && offsetY <= tableOffsetBottom && $t_fixed.is(":hidden"))
               $t_fixed.show();
            $t_fixed.css("left", tableOffsetLeft - offsetX + "px");
         }
         $(window).resize(resizeFixed);
         $(window).scroll(scrollFixed);
         init();
      });
   };
})(jQuery);

$(document).ready(function(){
   $("table").fixMe();
   $(".up").click(function() {
      $('html, body').animate({
      scrollTop: 0
   }, 2000);
 });
});

body{
  font:1.2em normal Arial,sans-serif;
  color:#34495E;
}

h1{
  text-align:center;
  text-transform:uppercase;
  letter-spacing:-2px;
  font-size:2.5em;
  margin:20px 0;
}

.container{
  width:90%;
  margin:auto;
}

table{
  border-collapse:collapse;
  width:100%;
}

.blue{
  border:2px solid #1ABC9C;
}

.blue thead{
  background:#1ABC9C;
}

.purple{
  border:2px solid #9B59B6;
}

.purple thead{
  background:#9B59B6;
}

thead{
  color:white;
}

th,td{
  text-align:center;
  padding:5px 0;
}

tbody tr:nth-child(even){
  background:#ECF0F1;
}

tbody tr:hover{
background:#BDC3C7;
  color:#FFFFFF;
}

.fixed{
  top:0;
  position:fixed;
  width:auto;
  display:none;
  border:none;
}

.scrollMore{
  margin-top:600px;
}

.up{
  cursor:pointer;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>&darr; SCROLL &darr;</h1>
<table class="blue">
  <thead>
    <tr>
      <th>Colonne 1</th>
      <th>Colonne 2</th>
      <th>Colonne 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Non</td>
      <td>MaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMaisMais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
       <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
  </tbody>
</table>

<h1 class="scrollMore">&darr; SCROLL MORE &darr;</h1>
<table class="purple">
  <thead>
    <tr>
      <th>Colonne 1</th>
      <th>Colonne 2</th>
      <th>Colonne 3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
       <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
    <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
       <tr>
      <td>Non</td>
      <td>Mais</td>
      <td>Allo !</td>
    </tr>
  </tbody>
</table>
<h1 class="up scrollMore">&uarr; UP &uarr;</h1>

Solution 7 - Jquery

There are many really good solution here already. But one of the simplest CSS only solutions that I use in these situations is as follows:

table {
  /* Not required only for visualizing */
  border-collapse: collapse;
  width: 100%;
}

table thead tr th {
  /* Important */
  background-color: red;
  position: sticky;
  z-index: 100;
  top: 0;
}

td {
  /* Not required only for visualizing */
  padding: 1em;
}

<table>
  <thead>
    <tr>
      <th>Col1</th>
      <th>Col2</th>
      <th>Col3</th>
    </tr>
  </thead>
  <tbody>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
  </tbody>
</table>

Because there is no requirement for JavaScript it simplifies the situation significantly. You essentially need to focus on the second CSS rule, which contains the conditions for ensuring that the head of the table remains of the top no matter the scroll space.

To elaborate on each of the rules in detail. position is meant to indicate to the browser that the head object, its row, and its cells all need to stick to the top. This necessarily needs to be accompanied by top, which specifies to the browser that the head will stick to the top of the page or viewport. Additionally, you can add z-index to ensure that the content of the head always remains on the top.

The background colour is merely to illustrate the point. You do not need to use any additional JavaScript to get this effect. This is supported in most major browsers after 2016.

Solution 8 - Jquery

Best solution is to use this jquery plugin:

https://github.com/jmosbech/StickyTableHeaders

This plugin worked great for us and we tried a lot other solutions. We tested it in IE, Chrome and Firefox

Solution 9 - Jquery

I found a simple solution without using JQuery and using CSS only.

You have to put the fixed contents inside 'th' tags and add the CSS

table th {
    position:sticky;
    top:0;
    z-index:1;
    border-top:0;
    background: #ededed;
}
   

The position, z-index and top properties are enough. But you can apply the rest to give for a better view.

Solution 10 - Jquery

I found a simple jQuery library called Sticky Table Headers. Two lines of code and it did exactly what I wanted. The solutions above don't manage the column widths, so if you have table cells that take up a lot of space, the resulting size of the persistent header will not match your table's width.

http://plugins.jquery.com/StickyTableHeaders/

Usage info here: https://github.com/jmosbech/StickyTableHeaders

Solution 11 - Jquery

you can use this approach, pure HTML and CSS no JS needed :)

.table-fixed-header {
  display: flex;
  justify-content: space-between;
  margin-right: 18px
}

.table-fixed {
  display: flex;
  justify-content: space-between;
  height: 150px;
  overflow: scroll;
}

.column {
  flex-basis: 24%;
  border-radius: 5px;
  padding: 5px;
  text-align: center;
}
.column .title {
  border-bottom: 2px grey solid;
  border-top: 2px grey solid;
  text-align: center;
  display: block;
  font-weight: bold;
}

.cell {
  padding: 5px;
  border-right: 1px solid;
  border-left: 1px solid;
}

.cell:nth-of-type(even) {
  background-color: lightgrey;
}

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Fixed header Bin</title>
</head>
<body>
<div class="table-fixed-header">
    
    <div class="column">
      <span class="title">col 1</span>
    </div>
    <div class="column">
      <span class="title">col 2</span>
    </div>
    <div class="column">
      <span class="title">col 3</span>
    </div>
    <div class="column">
      <span class="title">col 4</span>
    </div>
    
  </div>
  
  <div class="table-fixed">
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
    </div>
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
    </div>
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
      <div class="cell">beta</div>
      <div class="cell">beta</div>
      <div class="cell">beta</div>
      
    </div>
    
    <div class="column">
      <div class="cell">alpha</div>
      <div class="cell">beta</div>
      <div class="cell">ceta</div>
    </div>
    
  </div>
</body>
</html>

Solution 12 - Jquery

I too experienced the same issues with the border formatting not being shown using entrophy's code but a few little fixes and now the table is expandable and displays all css styling rules you may add.

to css add:

#maintable{width: 100%}    

then here is the new javascript:

    function moveScroll(){
    var scroll = $(window).scrollTop();
    var anchor_top = $("#maintable").offset().top;
    var anchor_bottom = $("#bottom_anchor").offset().top;
    if (scroll > anchor_top && scroll < anchor_bottom) {
        clone_table = $("#clone");
        if(clone_table.length === 0) {			
            clone_table = $("#maintable").clone();
            clone_table.attr({id: "clone"})
		    .css({
			    position: "fixed",
			    "pointer-events": "none",
                 top:0
		    })
		    .width($("#maintable").width());
		
            $("#table-container").append(clone_table);
		    // dont hide the whole table or you lose border style & 
		    // actively match the inline width to the #maintable width if the 
		    // container holding the table (window, iframe, div) changes width			
            $("#clone").width($("#maintable").width());
		    // only the clone thead remains visible
            $("#clone thead").css({
			    visibility:"visible"
		    });
		    // clone tbody is hidden
            $("#clone tbody").css({
			    visibility:"hidden"
		    });
		    // add support for a tfoot element
		    // and hide its cloned version too
		    var footEl = $("#clone tfoot");
		    if(footEl.length){
			    footEl.css({
				    visibility:"hidden"
			    });
		    }
        }
    } 
    else {
	    $("#clone").remove();
    }
}
$(window).scroll(moveScroll);

Solution 13 - Jquery

Here is a solution that builds upon the accepted answer. It corrects for: column widths, matching table style, and when the table is scrolled in a container div.

Usage

Ensure your table has a <thead> tag because only thead content will be fixed.

$("#header-fixed").fixHeader();

JavaSript

//Custom JQuery Plugin
(function ($) {
    $.fn.fixHeader = function () {
        return this.each(function () {
            var $table = $(this);
            var $sp = $table.scrollParent();
            var tableOffset = $table.position().top;
            var $tableFixed = $("<table />")
                .prop('class', $table.prop('class'))
                .css({ position: "fixed", "table-layout": "fixed", display: "none", "margin-top": "0px" });
            $table.before($tableFixed);
            $tableFixed.append($table.find("thead").clone());

            $sp.bind("scroll", function () {
                var offset = $(this).scrollTop();

                if (offset > tableOffset && $tableFixed.is(":hidden")) {
                    $tableFixed.show();
                    var p = $table.position();
                    var offset = $sp.offset();
                    
                    //Set the left and width to match the source table and the top to match the scroll parent
                    $tableFixed.css({ left: p.left + "px", top: (offset ? offset.top : 0) + "px", }).width($table.width());

                    //Set the width of each column to match the source table
                    $.each($table.find('th, td'), function (i, th) {
                        $($tableFixed.find('th, td')[i]).width($(th).width());
                    });

                }
                else if (offset <= tableOffset && !$tableFixed.is(":hidden")) {
                    $tableFixed.hide();
                }
            });
        });
    };
})(jQuery);

Solution 14 - Jquery

This will help you to have a fixed header which can also be scrolled horizontally with data.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Shubh</title>



<script type="text/javascript">
	var lastSeen = [ 0, 0 ];
	function checkScroll(div1, div2) {
		if (!div1 || !div2)
			return;
		var control = null;
		if (div1.scrollLeft != lastSeen[0])
			control = div1;
		else if (div2.scrollLeft != lastSeen[1])
			control = div2;
		if (control == null)
			return;
		else
			div1.scrollLeft = div2.scrollLeft = control.scrollLeft;
		lastSeen[0] = div1.scrollLeft;
		lastSeen[1] = div2.scrollLeft;
	}

	window
			.setInterval(
					"checkScroll(document.getElementById('innertablediv'), document.getElementById('headertable'))",
					1);
</script>

<style type="text/css">
#full {
	width: 400px;
	height: 300px;
}

#innertablediv {
	height: 200px;
	overflow: auto;
}

#headertable {
	overflow: hidden;
}
</style>
</head>
<body>

	<div id="full">




		<div id="headertable">
			<table border="1" bgcolor="grey" width="150px" id="headertable">
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>

					<td>&nbsp;&nbsp;&nbsp;</td>
				</tr>

			</table>
		</div>




		<div id="innertablediv">

			<table border="1" id="innertableid">
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>
				<tr>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
					<td>shubh, ansh</td>
				</tr>

			</table>
		</div>
	</div>
</body>
</html>

Solution 15 - Jquery

function fix_table_header_position(){
 var width_list = [];
 $("th").each(function(){
	width_list.push($(this).width());
 });
 $("tr:first").css("position", "absolute");
 $("tr:first").css("z-index", "1000");
 $("th, td").each(function(index){
	$(this).width(width_list[index]);
 });

 $("tr:first").after("<tr height=" + $("tr:first").height() + "></tr>");}

This is my solution

Solution 16 - Jquery

In this solution fixed header is created dynamically, the content and style is cloned from THEAD

all you need is two lines for example:

var $myfixedHeader = $("#Ttodo").FixedHeader(); //create fixed header $(window).scroll($myfixedHeader.moveScroll); //bind function to scroll event

My jquery plugin FixedHeader and getStyleObject provided below you can to put in the file .js

/* CSS - important only NOT transparent background */

#repTB{border-collapse: separate;border-spacing: 0;}

#repTb thead,#fixH thead{background: #e0e0e0 linear-gradient(#d8d8d8 0%, #e0e0e0 25%, #e0e0e0 75%, #d8d8d8 100%) repeat scroll 0 0;border:1px solid #CCCCCC;}

#repTb td{border:1px solid black}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>


<h3>example</h3> 
<table id="repTb">
<thead>
<tr><th>Col1</th><th>Column2</th><th>Description</th></tr>
</thead>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
<tr><td>info</td><td>info</td><td>info</td></tr>
</table>

// JAVASCRIPT

/*
 * getStyleObject Plugin for jQuery JavaScript Library
 * From: http://upshots.org/?p=112
 
Basic usage:
$.fn.copyCSS = function(source){
  var styles = $(source).getStyleObject();
  this.css(styles);
}
*/

(function($){
    $.fn.getStyleObject = function(){
        var dom = this.get(0);
        var style;
        var returns = {};
        if(window.getComputedStyle){
            var camelize = function(a,b){
                return b.toUpperCase();
            };
            style = window.getComputedStyle(dom, null);
            for(var i = 0, l = style.length; i < l; i++){
                var prop = style[i];
                var camel = prop.replace(/\-([a-z])/g, camelize);
                var val = style.getPropertyValue(prop);
                returns[camel] = val;
            };
            return returns;
        };
        if(style = dom.currentStyle){
            for(var prop in style){
                returns[prop] = style[prop];
            };
            return returns;
        };
        return this.css();
    }
})(jQuery);




//Floating Header of long table  PiotrC
(function ( $ ) {
    var tableTop,tableBottom,ClnH;
    $.fn.FixedHeader = function(){
        tableTop=this.offset().top,
        tableBottom=this.outerHeight()+tableTop;
        //Add Fixed header
        this.after('<table id="fixH"></table>');
        //Clone Header
        ClnH=$("#fixH").html(this.find("thead").clone());
        //set style
        ClnH.css({'position':'fixed', 'top':'0', 'zIndex':'60', 'display':'none',
        'border-collapse': this.css('border-collapse'),
		'border-spacing': this.css('border-spacing'),
        'margin-left': this.css('margin-left'),
        'width': this.css('width')            
        });
        //rewrite style cell of header
        $.each(this.find("thead>tr>th"), function(ind,val){
            $(ClnH.find('tr>th')[ind]).css($(val).getStyleObject());
        });
    return ClnH;}
    
    $.fn.moveScroll=function(){
        var offset = $(window).scrollTop();
        if (offset > tableTop && offset<tableBottom){
            if(ClnH.is(":hidden"))ClnH.show();
            $("#fixH").css('margin-left',"-"+$(window).scrollLeft()+"px");
        }
        else if (offset < tableTop || offset>tableBottom){
            if(!ClnH.is(':hidden'))ClnH.hide();
        }
    };
})( jQuery );





var $myfixedHeader = $("#repTb").FixedHeader();
$(window).scroll($myfixedHeader.moveScroll);

Solution 17 - Jquery

A bit late to the party, but here is an implementation that works with multiple tables on the same page and "jank" free (using requestAnimationFrame). Also there's no need to provide any width on the columns. Horizontal scrolling works as well.

The headers are defined in a div so you are free to add any markup there (like buttons), if required. This is all the HTML that is needed:

<div class="tbl-resp">
  <table id="tbl1" class="tbl-resp__tbl">
     <thead>
      <tr>
        <th>col 1</th>
        <th>col 2</th>
        <th>col 3</th>
      </tr>
    </thead> 
  </table>
</div>

https://jsfiddle.net/lloydleo/bk5pt5gs/

Solution 18 - Jquery

Create extra table with same header as the main table. Just put thead in the new table with one row and all the headers in it. Do position absolute and background white. For main table put it in a div and use some height and overflow-y scroll. This way our new table will overcome the header of main table and stay there. Surround everything in a div. Below is the rough code to do it.

      <div class="col-sm-8">

        <table id="header-fixed" class="table table-bordered table-hover" style="width: 351px;position: absolute;background: white;">
        <thead>
        <tr>
            <th>Col1</th>
            <th>Col2</th>
            <th>Col3</th>
        </tr>
    </thead>
      </table>


    <div style="height: 300px;overflow-y: scroll;">
          <table id="tableMain" class="table table-bordered table-hover" style="table-layout:fixed;overflow-wrap: break-word;cursor:pointer">
<thead>
    <tr>
      <th>Col1</th>
      <th>Col2</th>
      <th>Col3</th>
    </tr>
  </thead>
  <tbody>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
  </tbody>

            						</table>
              </div>
        </div>



			

Solution 19 - Jquery

div.wrapper {
    padding:20px;
}
table.scroll thead {
    width: 100%;
    background: #FC6822;
}
table.scroll thead tr:after {
    content: '';
    overflow-y: scroll;
    visibility: hidden;
}
table.scroll thead th {
    flex: 1 auto;
    display: block;
    color: #fff;
}
table.scroll tbody {
    display: block;
    width: 100%;
    overflow-y: auto;
    height: auto;
    max-height: 200px;
}
table.scroll thead tr,
table.scroll tbody tr {
    display: flex;
}
table.scroll tbody tr td {
    flex: 1 auto;
    word-wrap: break;
}
table.scroll thead tr th,
table.scroll tbody tr td {
    width: 25%;
    padding: 5px;
    text-align-left;
    border-bottom: 1px solid rgba(0,0,0,0.3);
}

<div class="wrapper">
    <table border="0" cellpadding="0" cellspacing="0" class="scroll">
        <thead>
            <tr>
                <th>Name</th>
                <th>Vorname</th>
                <th>Beruf</th>
                <th>Alter</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Müller</td>
                <td>Marie</td>
                <td>Künstlerin</td>
                <td>26</td>
            </tr>
            <tr>
                <td>Meier</td>
                <td>Stefan</td>
                <td>Chemiker</td>
                <td>52</td>
            </tr>
            <tr>
                <td>Schmidt</td>
                <td>Sabrine</td>
                <td>Studentin</td>
                <td>38</td>
            </tr>
            <tr>
                <td>Mustermann</td>
                <td>Max</td>
                <td>Lehrer</td>
                <td>41</td>
            </tr>
            <tr>
                <td>Müller</td>
                <td>Marie</td>
                <td>Künstlerin</td>
                <td>26</td>
            </tr>
            <tr>
                <td>Meier</td>
                <td>Stefan</td>
                <td>Chemiker</td>
                <td>52</td>
            </tr>
            <tr>
                <td>Schmidt</td>
                <td>Sabrine</td>
                <td>Studentin</td>
                <td>38</td>
            </tr>
            <tr>
                <td>Mustermann</td>
                <td>Max</td>
                <td>Lehrer</td>
                <td>41</td>
            </tr>
            <tr>
                <td>Müller</td>
                <td>Marie</td>
                <td>Künstlerin</td>
                <td>26</td>
            </tr>
            <tr>
                <td>Meier</td>
                <td>Stefan</td>
                <td>Chemiker</td>
                <td>52</td>
            </tr>
            <tr>
                <td>Schmidt</td>
                <td>Sabrine</td>
                <td>Studentin</td>
                <td>38</td>
            </tr>
            <tr>
                <td>Mustermann</td>
                <td>Max</td>
                <td>Lehrer</td>
                <td>41</td>
            </tr>
        </tbody>
    </table>
</div>

Demo: css fixed table header demo

Solution 20 - Jquery

Fix your issue with this

tbody {
  display: table-caption;
  height: 200px;
  caption-side: bottom;
  overflow: auto;
}

Solution 21 - Jquery

solution in my case, i have use only css for this:

first we nee table format like this :

<table>
<thead><tr><th></th></tr></thead>
<tbody><tr><td></td><tr></tbody>
</table>

And apply this css :

tbody {
    display:block;
    max-height:280px;
    overflow-x: hidden;
    overflow-y: auto;
	}
	thead, tbody tr {
	    display:table;
	    width:100%;
	    table-layout:fixed;
	}

and header is fixed

Solution 22 - Jquery

This can be achieved by using style property transform. All you have to do is wrapping your table into some div with fixed height and overflow set to auto, for example:

.tableWrapper {
  overflow: auto;
  height: calc( 100% - 10rem );
}

And then you can attach onscroll handler to it, here you have method that finds each table wrapped with <div class="tableWrapper"></div>:

  fixTables () {
    document.querySelectorAll('.tableWrapper').forEach((tableWrapper) => {
      tableWrapper.addEventListener('scroll', () => {
        var translate = 'translate(0,' + tableWrapper.scrollTop + 'px)'
        tableWrapper.querySelector('thead').style.transform = translate
      })
    })
  }

And here is working example of this in action (i have used bootstrap to make it prettier): fiddle

For those who also want to support IE and Edge, here is the snippet:

  fixTables () {
    const tableWrappers = document.querySelectorAll('.tableWrapper')
    for (let i = 0, len = tableWrappers.length; i < len; i++) {
      tableWrappers[i].addEventListener('scroll', () => {
        const translate = 'translate(0,' + tableWrappers[i].scrollTop + 'px)'
        const headers = tableWrappers[i].querySelectorAll('thead th')
        for (let i = 0, len = headers.length; i < len; i++) {
          headers[i].style.transform = translate
        }
      })
    }
  }

In IE and Edge scroll is a little bit laggy... but it works

Here is answer which helps me to find out this: answer

Solution 23 - Jquery

I've tried most of these solutions, and eventually found (IMO) the best, modern, solution:

CSS grids


With CSS grids, you can define a 'grid', and you can finally create a nice, javascript-free, cross-browser solution for a table with a fixed header, and scrollable content. The header height can even dynamic.

CSS: Display as grid, and set the number of template-rows:

.grid {
	display: grid;
	grid-template-rows: 50px auto; // For fixed height header
	grid-template-rows: auto auto; // For dynamic height header
}

HTML: Create a grid container and the number of defined rows:

<div class="grid">
	<div></div>
	<div></div>
</div>

Here is working example:

CSS

body {
  margin: 0px;
  padding: 0px;
  text-align: center;
}

.table {
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-rows: 50px auto;
}
.table-heading {
  background-color: #ddd;
}
.table-content {
  overflow-x: hidden;
  overflow-y: scroll;
}

HTML

<html>
	<head>
	</head>
	<body>
		<div class="table">
			<div class="table-heading">
				HEADING
			</div>
			<div class="table-content">
				CONTENT - CONTENT - CONTENT <br/>
				CONTENT - CONTENT - CONTENT <br/>
				CONTENT - CONTENT - CONTENT <br/>
				CONTENT - CONTENT - CONTENT <br/>
				CONTENT - CONTENT - CONTENT <br/>
				CONTENT - CONTENT - CONTENT <br/>
			</div>
		</div>
	</body>
</html>

Solution 24 - Jquery

I have tried it using transformation:translate. While it works good in Firefox and Chrome, there is simply no function in IE11. No double scroll bars. Supports table tfoot and caption. Pure Javascript, no jQuery.

http://jsfiddle.net/wbLqzrfb/42/

thead.style.transform="translate(0,"+(dY-top-1)+"px)";

Solution 25 - Jquery

I found a solution without jquery

HTML

<table class="fixed_header">
  <thead>
    <tr>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
      <th>Col 4</th>
      <th>Col 5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>row 1-0</td>
      <td>row 1-1</td>
      <td>row 1-2</td>
      <td>row 1-3</td>
      <td>row 1-4</td>
    </tr>
    <tr>
      <td>row 2-0</td>
      <td>row 2-1</td>
      <td>row 2-2</td>
      <td>row 2-3</td>
      <td>row 2-4</td>
    </tr>
    <tr>
      <td>row 3-0</td>
      <td>row 3-1</td>
      <td>row 3-2</td>
      <td>row 3-3</td>
      <td>row 3-4</td>
    </tr>
    <tr>
      <td>row 4-0</td>
      <td>row 4-1</td>
      <td>row 4-2</td>
      <td>row 4-3</td>
      <td>row 4-4</td>
    </tr>
    <tr>
      <td>row 5-0</td>
      <td>row 5-1</td>
      <td>row 5-2</td>
      <td>row 5-3</td>
      <td>row 5-4</td>
    </tr>
    <tr>
      <td>row 6-0</td>
      <td>row 6-1</td>
      <td>row 6-2</td>
      <td>row 6-3</td>
      <td>row 6-4</td>
    </tr>
    <tr>
      <td>row 7-0</td>
      <td>row 7-1</td>
      <td>row 7-2</td>
      <td>row 7-3</td>
      <td>row 7-4</td>
    </tr>
  </tbody>
</table>

CSS

.fixed_header{
    width: 400px;
    table-layout: fixed;
    border-collapse: collapse;
}

.fixed_header tbody{
  display:block;
  width: 100%;
  overflow: auto;
  height: 100px;
}

.fixed_header thead tr {
   display: block;
}

.fixed_header thead {
  background: black;
  color:#fff;
}

.fixed_header th, .fixed_header td {
  padding: 5px;
  text-align: left;
  width: 200px;
}

You can see it working here: https://jsfiddle.net/lexsoul/fqbsty3h

Source: https://medium.com/@vembarrajan/html-css-tricks-scroll-able-table-body-tbody-d23182ae0fbc

Solution 26 - Jquery

Most simple CSS, this will work single and multi rows of table headers too

 /* CSS */

.fix-head-table thead {	
    position: sticky;
    top: 0;
}
.fix-head-table thead th{ 
    border: 0px; 
    box-shadow: inset -2.5px 1px 0px 0px black !important;
    box-sizing: border-box;
}

You can modify box-shadow inset in developer window css(inspect element) as your need and also color of that

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
Questionuser2385136View Question on Stackoverflow
Solution 1 - JqueryAndrew WhitakerView Answer on Stackoverflow
Solution 2 - JqueryIhor ZenichView Answer on Stackoverflow
Solution 3 - JqueryentropyView Answer on Stackoverflow
Solution 4 - JquerymkoryakView Answer on Stackoverflow
Solution 5 - JqueryrockusbacchusView Answer on Stackoverflow
Solution 6 - JqueryjosephtingView Answer on Stackoverflow
Solution 7 - JquerySrivats ShankarView Answer on Stackoverflow
Solution 8 - JqueryJanningView Answer on Stackoverflow
Solution 9 - JqueryAbhi DasView Answer on Stackoverflow
Solution 10 - JqueryLok Yan WongView Answer on Stackoverflow
Solution 11 - JqueryxelilofView Answer on Stackoverflow
Solution 12 - Jqueryseeit360View Answer on Stackoverflow
Solution 13 - JqueryBen GripkaView Answer on Stackoverflow
Solution 14 - Jqueryuser2669926View Answer on Stackoverflow
Solution 15 - JqueryJustWeView Answer on Stackoverflow
Solution 16 - JqueryPiotr C.View Answer on Stackoverflow
Solution 17 - JqueryLeo Lloyd AndradeView Answer on Stackoverflow
Solution 18 - JqueryNovice_JSView Answer on Stackoverflow
Solution 19 - Jqueryrevilodesign.deView Answer on Stackoverflow
Solution 20 - JqueryJ Benjamín SamayoaView Answer on Stackoverflow
Solution 21 - JquerySandip SolankiView Answer on Stackoverflow
Solution 22 - JqueryDaniel BudzyńskiView Answer on Stackoverflow
Solution 23 - JqueryJeffrey RoosendaalView Answer on Stackoverflow
Solution 24 - JqueryHenrik HaftmannView Answer on Stackoverflow
Solution 25 - JqueryLexsoulView Answer on Stackoverflow
Solution 26 - JqueryVigneshKarthik KasiviswanathanView Answer on Stackoverflow