HTML table with fixed headers?

Question: Is there a cross-browser CSS/JavaScript technique to display a long HTML table such that the column headers stay fixed on-screen and do not scroll with the table body. Think of the "freeze panes" effect in Microsoft Excel.
I want to be able to scroll through the contents of the table, but to always be able to see the column headers at the top.
Comment: Try this: [Pure CSS Scrollable Table with Fixed Header](http://www.imaputz.com/cssStuff/bigFourVersion.html) **EDIT**: This one should work in Internet Explorer 7 as seen in the [example](http://rcs-comp.com/blog/scrolling_table/): [Scrolling HTML Table with Fixed Header](http://rcswebsolutions.wordpress.com/2007/01/02/scrolling-html-table-with-fixed-header/) **EDIT 2:** I found a couple of extra links that could be of use: - [Stupid fixed header](http://jackysee.googlepages.com/fixedheaders.html) - A jQuery plugin with some limitations. - [Fixed Table Headers](http://cross-browser.com/x/examp
Comment: I've come across many solution which generally works but none of them worked scrolling div. I mean, your table is inside a scrollable div and still you want your table header still inside that div. I've solved that and share the [solution here](http://rajputyh.blogspot.in/2011/12/floatingfixed-table-header-in-html-page.html).
Comment: Solution:
http://stackoverflow.com/questions/2382083/fix-thead-on-page-scroll/21829562#21829562
Comment: https://github.com/karaxuna/fixed-table-header
Comment: `sticky` seems to be the best solution. There's also a nice answer on a similar question with a helpful example of it: https://stackoverflow.com/a/50516259/982107
Answer: This can be cleanly solved in four lines of code.
If you only care about modern browsers, a fixed header can be achieved much easier by using CSS transforms. Sounds odd, but works great:

HTML and CSS stay as-is.
No external JavaScript dependencies.
Four lines of code.
Works for all configurations (table-layout: fixed, etc.).

document.getElementById("wrap").addEventListener("scroll", function(){
   var translate = "translate(0,"+this.scrollTop+"px)";
   this.querySelector("thead").style.transform = translate;
});

Support for CSS transforms is widely available except for Internet Explorer 8-.
Here is the full example for reference:

document.getElementById("wrap").addEventListener("scroll",function(){
   var translate = "translate(0,"+this.scrollTop+"px)";
   this.querySelector("thead").style.transform = translate;
});
/* Your existing container */
#wrap {
    overflow: auto;
    height: 400px;
}

/* CSS for demo */
td {
    background-color: green;
    width: 200px;
    height: 100px;
}

                Foo
                Bar

Comment: Found out, it works but have to apply the transform to th / td, not thead.
Comment: I have to say that, my previous comment notwithstanding, this is the closest thing to a perfect solution I've seen. Even the horizontal scrolling is perfect (better than my own solution). Here's an example with borders (you can't use border-collapse) and a scrollbar that sticks to the table instead of the container: [jsfiddle](http://jsfiddle.net/doctorDestructo/gfbk73bp/)
Comment: This does not work in any version of IE or in Edge. Here is a version that does based on @redhead's comment https://jsfiddle.net/n6o8ocwb/2/
Comment: @AlexAlexeev, your solution is amazing. Thank you. I noticed that, the resulting fixed header does not have the border  lines that distinguish columns. The default CSS style is lost. Even when I include this ... `$(this).addClass('border')` changes the rest of the table features fonts, size , color that I pass in the border class. But, does not add lines to the fixed header. Appreciate, any inputs on how to fix this
Comment: @user5249203 I know you asked a few month ago but I had the same problem and it was due to border-collapse: see this : http://stackoverflow.com/questions/33777751/combination-of-border-collapsecollapse-and-transformtranslate.
Comment: Amazing! 2 Tweaks I did: 1) I set a `background-color` for my table headers e.g.`th { background-color:#fff; }` to get rid of the overlap beneath the header when scrolling. 2) I subtracted 3 from `this.scrollTop` (e.g. `var translate = "translate(0,"+(this.scrollTop-3)+"px)";`) because the rows scrolled up a couple pixels above the header before disappearing. I'm not sure if these were caveats based on my other markup, but maybe they will be useful to someone else! (looking at you, @GregVeres !)
Comment: Wow! Ok, after more searching, in 2018 it seems there is actually an appropriate solution. Forget all of this magic. Just use the following CSS: `thead th { position: sticky; top: 0; }`
Comment: Unfortunately, it's not working for me in IE 11, with or without vendor prefixes :(
Comment: Any way we can `translate` the border of the `thead` and `th` as well along with the thead?
Comment: @MikeResoli yes, this is the code I came to: https://jsfiddle.net/buduguru/m1jvyafz/
Comment: Love the idea, and it works well in the Fiddle. On my implementation, however, the scroll performance is awful. Anyone else dealing with this?
Comment: I love this solution so much. It's perfect and looks to the user as if the browser supports a fixed column header natively. I ran into the border-collapse bug (ie: the translate caused the borders to disappear). I fixed it by using border-spacing: 0; instead of border-collapse.
Comment: Does not work good on mobile. Maybe because scroll event does not fire immediately.
Comment: Solution for the border problem: use `border-spacing: 0;` (instead of `border-collapse`), and then only set `border-bottom` and `border-right` for the cells to avoid double thick borders. Of course, also disable `border-right` for the last cell in each row and `border-bottom` for the last row in the table.
Comment: You would need to add vendor prefixes to make this work in some browsers (e.g. IE9, Safari, some mobile browsers), right? Also, I couldn't get it to work in IE9 even with a prefix, but maybe I'm doing something wrong: [jsfiddle](http://jsfiddle.net/doctorDestructo/voy3rv0h/)
Comment: I would be glad to know how to use this technic for a fixed column !
Comment: Great solution. Thank you! Could you also help to get the first column fixed?
Comment: @AlexAlexeev I also need a hand with this. Have you found any solutions over the past fews months? +1 for this solution, it is 100% the best I've found.
Comment: @user5249203 could you create a JSFiddle? :)
Comment: So many hours of research and you solved the issue we were butting our heads on. Gorgeous. Managed to make this work in all the browsers.
Comment: Does anyone is having a solution for IE 11? Is not working there.
Comment: @AlexAlexeev How can I adopt your great solution to a case in which I have a complex headers including 2 headers rows, with with rowspan and colspan  attributes? Thanks
Comment: @Maximilian Hils. Unfortunatley I couldn't edit the last comment after 5 minutes, so I fix the addressee אם Maximilian, and of course any other one who can advise... :)
Comment: Thanks for this. It seems that we should be translating th, not thead though and doing it with a querySelectorAll('th') so that IE and Edge work. This also works in the other browsers. BUT, why is the background of the TH now transparent? Even in your code snippet, I am seeing the green show through behind the words of the header. I want to have an opaque header but I am having difficulty getting it to be opaque. Any help would be greatly appreciated.
Comment: This idea was used to have a fixed table header AND pinned columns using primarily CSS. See here: https://stackoverflow.com/questions/45314977/how-to-create-an-html-table-that-has-a-fixed-header-and-pinned-columns-usi
Comment: Gah... I spoke too soon. It works *absolutely perfectly* on Chrome, but I'm getting flickering on other browsers. The flickering solutions mentioned in other answers work, but only by "hiding" the header during scroll, which is ok but not really great. It's pretty incredible that it's so hard to solve this absolutely basic UI requirement...
Comment: needs a fixed height damn, how do you tackle height 100% with this
Comment: this is usable in fixed table header. Also working in IE11 , but only on . Thanks.
Comment: @archz Thanks, mate. Used the answer here (https://stackoverflow.com/a/33778236/10491012) to solve the border collapse issue. Also made the thead 2px border in order to hide the nasty overlap, that was visible in Firefox, when scrolling. I didn't like Crayon Violent's solution, because it caused a gap between the thead and tbody.
Comment: Not working for me, Chrome 2022.
Comment: This works, but the transformation is laggy.
Answer: I was looking for a solution for this for a while and found most of the answers are not working or not suitable for my situation, so I wrote a simple solution with jQuery.
This is the solution outline:

Clone the table that needs to have a fixed header, and place the
cloned copy on top of the original.
Remove the table body from top table.
Remove the table header from bottom table.
Adjust the column widths. (We keep track of the original column widths)

Below is the code in a runnable demo.

function scrolify(tblAsJQueryObject, height) {
  var oTbl = tblAsJQueryObject;

  // for very large tables you can remove the four lines below
  // and wrap the table with
 in the mark-up and assign
  // height and overflow property
  var oTblDiv = $("
");
  oTblDiv.css('height', height);
  oTblDiv.css('overflow', 'scroll');
  oTbl.wrap(oTblDiv);

  // save original width
  oTbl.attr("data-item-original-width", oTbl.width());
  oTbl.find('thead tr td').each(function() {
    $(this).attr("data-item-original-width", $(this).width());
  });
  oTbl.find('tbody tr:eq(0) td').each(function() {
    $(this).attr("data-item-original-width", $(this).width());
  });

  // clone the original table
  var newTbl = oTbl.clone();

  // remove table header from original table
  oTbl.find('thead tr').remove();
  // remove table body from new table
  newTbl.find('tbody tr').remove();

  oTbl.parent().parent().prepend(newTbl);
  newTbl.wrap("
");

  // replace ORIGINAL COLUMN width
  newTbl.width(newTbl.attr('data-item-original-width'));
  newTbl.find('thead tr td').each(function() {
    $(this).width($(this).attr("data-item-original-width"));
  });
  oTbl.width(oTbl.attr('data-item-original-width'));
  oTbl.find('tbody tr:eq(0) td').each(function() {
    $(this).width($(this).attr("data-item-original-width"));
  });
}

$(document).ready(function() {
  scrolify($('#tblNeedsScrolling'), 160); // 160 is height
});

Header 1	Header 2

row 1, cell 1	row 1, cell 2

row 2, cell 1	row 2, cell 2

row 3, cell 1	row 3, cell 2

row 4, cell 1	row 4, cell 2

row 5, cell 1	row 5, cell 2

row 6, cell 1	row 6, cell 2

row 7, cell 1	row 7, cell 2

row 8, cell 1	row 8, cell 2

This solution works in Chrome and IE. Since it is based on jQuery, this should work in other jQuery supported browsers as well.
Comment: and how can we solce the problem when the content is bigger than the width?
Comment: Unfortunately, if you require pixel-perfect alignment of the columns, this doesn't work: http://jsbin.com/elekiq/1 ([source code](http://jsbin.com/elekiq/1/edit)). You can see that some headers are offset from where they should be, just slightly. The effect is more obvious if you're using backgrounds: http://jsbin.com/elekiq/2 ([source code](http://jsbin.com/elekiq/2/edit)). (I was working along these same lines, ran into this in my code, found yours and thought "Oh, I wonder if he's solved that for me!" Sadly not. :-) ) Browsers are SUCH a pain about wanting to control the widths of cells...
Comment: @tetra td {  max-width: 30px; }

this will allow you the developer to control how the rows are displayed.
Comment: This doesn't seem to work with horizontal scrolling- it creates the header, but it extends beyond the scrollable area (visibly) and doesn't scroll with the content.
Comment: But what if the contents in some header cell are longer than in td cells? I tried that in IE7, and width() breaks everything. IE8 and IE9   work fine, though...
Comment: This one works, But it will produce invalid HTML. the `.clone` method is dangerous unless you really know what it doing...
Comment: Also what you mean by `thead tr td` selector ? its looks like invalid as per your markup.
Comment: Resizing the window causes the issue: http://jsfiddle.net/2z7r4o96/3/ and here is my question: http://stackoverflow.com/questions/28412593/how-to-match-the-width-of-a-cloned-th-with-that-of-the-table-cell. Please help me :) Thanks.
Comment: The demo breaks when you have a table requiring horizontal scrolling, and also when columns are different sizes. I wrote a version which fixes these edge cases and also doesn't require jQuery: https://github.com/OliverJAsh/sticky-table-header
Answer: I've just completed putting together a jQuery plugin that will take valid single table using valid HTML (have to have a thead and tbody) and will output a table that has fixed headers, optional fixed footer that can either be a cloned header or any content you chose (pagination, etc.).  If you want to take advantage of larger monitors it will also resize the table when the browser is resized.  Another added feature is being able to side scroll if the table columns can not all fit in view.
http://fixedheadertable.com/
on github: http://markmalek.github.com/Fixed-Header-Table/
It's extremely easy to setup and you can create your own custom styles for it.  It also uses rounded corners in all browsers.  Keep in mind I just released it, so it's still technically beta and there are very few minor issues I'm ironing out.
It works in Internet Explorer 7, Internet Explorer 8, Safari, Firefox and Chrome.
Comment: The latest version doesn't work in IE6. I no longer support IE6.
Comment: Thanks! I'm adding a new release later today when I get home from work.  Here is a link to my blog entry with what i'm adding: http://fixedheadertable.mmalek.com/2009/10/07/adding-new-features-and-bug-fixes-today/
Comment: Thank you for this.  I know this question is over a year old, but even at the risk of stirring up settled silt, I would like to tell you that your work is appreciated
Comment: In your demo, the widths are off in ie6 :-( table header and body are not aligned.
Comment: great work Mark - unfortunatelly there are some problems with the scrolling of the fixed header and column in mobile devices (iPad, Android tablet) - when I scroll the content those fixed parts don't scroll - when I stop scrolling and tap once the table, the fixed parts "jump" to proper positions - is there a simple way to fix this?
Comment: Mark - this plugin doesn't work with jQuery 1.9.1. Any plans to update it? I also couldn't get the table to resize with the browser in any version of jQuery. It looks promising though, Thanks.
Comment: If (like me) you could not get this working, please ensure CSS is correctly added, see [this question and answer](http://stackoverflow.com/questions/23651011/fixed-header-table-does-not-scroll-jquery).
Answer: I also created a plugin that addresses this issue. My project - jQuery.floatThead has been around for over 4 years now and is very mature.
It requires no external styles and does not expect your table to be styled in any particular way. It supports Internet Explorer9+ and Firefox/Chrome.
Currently (2018-05) it has:

405 commits and 998 stars on GitHub

Many (not all) of the answers here are quick hacks that may have solved the problem one person was having, but will work not for every table.
Some of the other plugins are old and probably work great with Internet Explorer, but will break on Firefox and Chrome.
Comment: Great. Thanks a lot. The plugin worked well in Firefox 45.2, Chromium 51 and IE 11. Also, it does not interfere with a lot of JS and jQuery code built on the same page.
Answer: All of the attempts to solve this from outside the CSS specification are pale shadows of what we really want: Delivery on the implied promise of THEAD.
This frozen-headers-for-a-table issue has been an open wound in HTML/CSS for a long time.
In a perfect world, there would be a pure-CSS solution for this problem. Unfortunately, there doesn't seem to be a good one in place.
Relevant standards-discussions on this topic include:

The Sticky Positioning proposal at www-style: http://lists.w3.org/Archives/Public/www-style/2012Jun/0627.html
Tab Atkins' proposal for position-root, position-contain or position-restrict:  http://www.xanthir.com/blog/b48H0

UPDATE: Firefox shipped position:sticky in version 32. Everyone wins!
Comment: You can see an example of this [working over on CSS-Tricks](https://css-tricks.com/position-sticky-and-table-headers/).
Answer: TL;DR
If you target modern browsers and don't have extravagant styling needs: http://jsfiddle.net/dPixie/byB9d/3/ ... Although the big four version is pretty sweet as well this version handles fluid width a lot better.
Good news everyone!
With the advances of HTML5 and CSS3 this is now possible, at least for modern browsers. The slightly hackish implementation I came up with can be found here: http://jsfiddle.net/dPixie/byB9d/3/. I have tested it in FX 25, Chrome 31 and IE 10 ...
Relevant HTML (insert a HTML5 doctype at the top of your document though):

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

section {
  position: relative;
  border: 1px solid #000;
  padding-top: 37px;
  background: #500;
}

section.positioned {
  position: absolute;
  top: 100px;
  left: 100px;
  width: 800px;
  box-shadow: 0 0 15px #333;
}

.container {
  overflow-y: auto;
  height: 200px;
}

table {
  border-spacing: 0;
  width: 100%;
}

td+td {
  border-left: 1px solid #eee;
}

td,
th {
  border-bottom: 1px solid #eee;
  background: #ddd;
  color: #000;
  padding: 10px 25px;
}

th {
  height: 0;
  line-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  color: transparent;
  border: none;
  white-space: nowrap;
}

th div {
  position: absolute;
  background: transparent;
  color: #fff;
  padding: 9px 25px;
  top: 0;
  margin-left: -25px;
  line-height: normal;
  border-left: 1px solid #800;
}

th:first-child div {
  border: none;
}

            Table attribute name

Table attribute name

            Value

Value

            Description

Description

          align
          left, center, right
          Not supported in HTML5. Deprecated in HTML 4.01. Specifies the alignment of a table according to surrounding text

          bgcolor
          rgb(x,x,x), #xxxxxx, colorname
          Not supported in HTML5. Deprecated in HTML 4.01. Specifies the background color for a table

          border
          1,""
          Specifies whether the table cells should have borders or not

          cellpadding
          pixels
          Not supported in HTML5. Specifies the space between the cell wall and the cell content

          cellspacing
          pixels
          Not supported in HTML5. Specifies the space between cells

          frame
          void, above, below, hsides, lhs, rhs, vsides, box, border
          Not supported in HTML5. Specifies which parts of the outside borders that should be visible

          rules
          none, groups, rows, cols, all
          Not supported in HTML5. Specifies which parts of the inside borders that should be visible

          summary
          text
          Not supported in HTML5. Specifies a summary of the content of a table

          width
          pixels, %
          Not supported in HTML5. Specifies the width of a table

But how?!
Simply put you have a table header, that you visually hide by making it 0px high, that also contains divs used as the fixed header. The table's container leaves enough room at the top to allow for the absolutely positioned header, and the table with scrollbars appear as you would expect.
The code above uses the positioned class to position the table absolutely (I'm using it in a popup style dialog) but you can use it in the flow of the document as well by removing the positioned class from the container.
But ...
It's not perfect. Firefox refuses to make the header row 0px (at least I did not find any way) but stubbornly keeps it at minimum 4px ... It's not a huge problem, but depending on your styling it will mess with your borders etc.
The table is also using a faux column approach where the background color of the container itself is used as the background for the header divs, that are transparent.
Summary
All in all there might be styling issues depending on your requirements, especially borders or complicated backgrounds. There might also be problems with computability, I haven't checked it in a wide variety of browsers yet (please comment with your experiences if you try it out), but I didn't find anything like it so I thought it was worth posting anyway ...
Comment: Worth pointing out that this requires a design where a fixed-height table can be specified.
Comment: @Cheekysoft - No, the table and the row content can flow freely. The container, in my example the `` element, needs to be height constrained only to force it to overflow and show the scrolling. Any layout that will make the container overflow would work. If you find a case where it doesn't please post a link to a fiddle.
Comment: If you shrink the width of the window until horizontal scrolling kicks in, then the header does not scroll horizontally with the body.  Darn.
Comment: @dlaliberte - Well, since the header and table are actually two different elements you can, ofc, get into weirdness. But my example doesn't allow overflow on the table columns and the headers are usually easier to control than the table content. That said, if you cause the header to "overflow" if will stick out to the right of the table and look severely broken. You could fix this by setting a min width on the table, forcing it to overflow the page as well ... But it's a hack so it will never be perfect...
Comment: The hard coded `padding-top` value also means that if the table heading text is on more than one line it'll appear on top of the table cells. Pity, because this works like a charm most of the time. Really nice trick with the `div` in the `th` to get around the column sizing issue most other solutions have.
Answer: The CSS property position: sticky has great support in most modern browsers (I had issues with Edge, see below).
This lets us solve the problem of fixed headers quite easily:
thead th { position: sticky; top: 0; }

Safari needs a vendor prefix: -webkit-sticky.
For Firefox, I had to add min-height: 0 to one the parent elements. I forget exactly why this was needed.
Most unfortunately, the Microsoft Edge implementation seems to be only semi-working. At least, I had some flickering and misaligned table cells in my testing. The table was still usable, but had significant aesthetic issues.
Comment: Using `position: sticky;` with the table inside a div that has `overflow: scroll;`, `overflow-x: scroll;`, or `overflow-y: scroll;`. appears to be the best and simplest solution for fixed table headers and columns in modern browsers. This answer needs to be voted to the top.
Comment: Excellent solution, just need to make sure to set background-color to something other than transparent.
Answer: Here is a jQuery plugin for fixed table headers. It allows the entire page to scroll, freezing the header when it reaches the top. It works well with Twitter Bootstrap tables.
GitHub repository: https://github.com/oma/table-fixed-header
It does not scroll only table content. Look to other tools for that, as one of these other answers. You decide what fits your case the best.
Answer: Most of the solutions posted here require jQuery. If you are looking for a framework independent solution try Grid: http://www.matts411.com/post/grid/
It's hosted on Github here: https://github.com/mmurph211/Grid
Not only does it support fixed headers, it also supports fixed left columns and footers, among other things.
Comment: Hey I know you! We seemed to have written very similar shit (https://github.com/mkoryak/floatThead) - Misha
Comment: This is really neat if it meets your needs, I just played with it today.  Unfortunately, it is rather a rectangular grid (as the name implies, actually) and not a true table with row height adjusted by contents.  And styling individual rows seemed difficult.  I couldn't manage to create a zebra striped table but didn't try very hard as my needs were actually more complex.  Anyway, nice work.
Answer: A more refined pure CSS scrolling table
All of the pure CSS solutions I've seen so far-- clever though they may be-- lack a certain level of polish, or just don't work right in some situations. So, I decided to create my own...
Features:

It's pure CSS, so no jQuery required (or any JavaScript code at all, for that
matter)
You can set the table width to a percent (a.k.a. "fluid") or a fixed value, or let the content determine its width (a.k.a. "auto")
Column widths can also be fluid, fixed, or auto.
Columns will never become misaligned with headers due to horizontal scrolling (a problem that occurs in every other CSS-based solution I've seen that doesn't require fixed widths).
Compatible with all of the popular desktop browsers, including Internet Explorer back to version 8
Clean, polished appearance; no sloppy-looking 1-pixel gaps or misaligned borders; looks the same in all browsers

Here are a couple of fiddles that show the fluid and auto width options:

Fluid Width and Height (adapts to screen size): jsFiddle (Note that the scrollbar only shows up when needed in this configuration, so you may have to shrink the frame to see it)

Auto Width, Fixed Height (easier to integrate with other content): jsFiddle

The Auto Width, Fixed Height configuration probably has more use cases, so I'll post the code below.

/* The following 'html' and 'body' rule sets are required only
   if using a % width or height*/

/*html {
  width: 100%;
  height: 100%;
}*/

body {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 20px 0 20px;
  text-align: center;
}
.scrollingtable {
  box-sizing: border-box;
  display: inline-block;
  vertical-align: middle;
  overflow: hidden;
  width: auto; /* If you want a fixed width, set it here, else set to auto */
  min-width: 0/*100%*/; /* If you want a % width, set it here, else set to 0 */
  height: 188px/*100%*/; /* Set table height here; can be fixed value or % */
  min-height: 0/*104px*/; /* If using % height, make this large enough to fit scrollbar arrows + caption + thead */
  font-family: Verdana, Tahoma, sans-serif;
  font-size: 16px;
  line-height: 20px;
  padding: 20px 0 20px 0; /* Need enough padding to make room for caption */
  text-align: left;
  color: black;
}
.scrollingtable * {box-sizing: border-box;}
.scrollingtable > div {
  position: relative;
  border-top: 1px solid black;
  height: 100%;
  padding-top: 20px; /* This determines column header height */
}
.scrollingtable > div:before {
  top: 0;
  background: cornflowerblue; /* Header row background color */
}
.scrollingtable > div:before,
.scrollingtable > div > div:after {
  content: "";
  position: absolute;
  z-index: -1;
  width: 100%;
  height: 100%;
  left: 0;
}
.scrollingtable > div > div {
  min-height: 0/*43px*/; /* If using % height, make this large
                            enough to fit scrollbar arrows */
  max-height: 100%;
  overflow: scroll/*auto*/; /* Set to auto if using fixed
                               or % width; else scroll */
  overflow-x: hidden;
  border: 1px solid black; /* Border around table body */
}
.scrollingtable > div > div:after {background: white;} /* Match page background color */
.scrollingtable > div > div > table {
  width: 100%;
  border-spacing: 0;
  margin-top: -20px; /* Inverse of column header height */
  /*margin-right: 17px;*/ /* Uncomment if using % width */
}
.scrollingtable > div > div > table > caption {
  position: absolute;
  top: -20px; /*inverse of caption height*/
  margin-top: -1px; /*inverse of border-width*/
  width: 100%;
  font-weight: bold;
  text-align: center;
}
.scrollingtable > div > div > table > * > tr > * {padding: 0;}
.scrollingtable > div > div > table > thead {
  vertical-align: bottom;
  white-space: nowrap;
  text-align: center;
}
.scrollingtable > div > div > table > thead > tr > * > div {
  display: inline-block;
  padding: 0 6px 0 6px; /*header cell padding*/
}
.scrollingtable > div > div > table > thead > tr > :first-child:before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 20px; /*match column header height*/
  border-left: 1px solid black; /*leftmost header border*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div > div:first-child,
.scrollingtable > div > div > table > thead > tr > * + :before {
  position: absolute;
  top: 0;
  white-space: pre-wrap;
  color: white; /*header row font color*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div[label]:after {content: attr(label);}
.scrollingtable > div > div > table > thead > tr > * + :before {
  content: "";
  display: block;
  min-height: 20px; /* Match column header height */
  padding-top: 1px;
  border-left: 1px solid black; /* Borders between header cells */
}
.scrollingtable .scrollbarhead {float: right;}
.scrollingtable .scrollbarhead:before {
  position: absolute;
  width: 100px;
  top: -1px; /* Inverse border-width */
  background: white; /* Match page background color */
}
.scrollingtable > div > div > table > tbody > tr:after {
  content: "";
  display: table-cell;
  position: relative;
  padding: 0;
  border-top: 1px solid black;
  top: -1px; /* Inverse of border width */
}
.scrollingtable > div > div > table > tbody {vertical-align: top;}
.scrollingtable > div > div > table > tbody > tr {background: white;}
.scrollingtable > div > div > table > tbody > tr > * {
  border-bottom: 1px solid black;
  padding: 0 6px 0 6px;
  height: 20px; /* Match column header height */
}
.scrollingtable > div > div > table > tbody:last-of-type > tr:last-child > * {border-bottom: none;}
.scrollingtable > div > div > table > tbody > tr:nth-child(even) {background: gainsboro;} /* Alternate row color */
.scrollingtable > div > div > table > tbody > tr > * + * {border-left: 1px solid black;} /* Borders between body cells */

Top Caption

Column 4

Column 4

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

Lorem ipsum	Dolor	Sit	Amet consectetur

    Faux bottom caption

The method I used to freeze the header row is similar to d-Pixie's, so refer to his post for an explanation. There were a slew of bugs and limitations with that technique that could only be fixed with heaps of additional CSS and an extra div container or two.
Comment: @JustinSane Glad you like it! I would guess the lack of appreciation is due to the fact that it shares the page with [Maximilian Hils' amazing solution](https://stackoverflow.com/a/25902860/2759272). If you're not opposed to using a tiny bit of JS, you should definitely check it out.
Comment: This answer is way underappreciated! I spent days trying to get other solutions to work for my especially annoying case. Every single one of them failed to stay aligned in one way or another. This finally did it!

Seems overly complicated at first, but once you get the hang of it, awesome. You can remove quite a bit of stuff you don't need in the end, when not using fluid width, etc.
Comment: Damn, that **is** an almost perfect solution indeed. I was using jQuery anyway, tried to get it working with that before I found yours (via your comment to another question). Wasn't thinking of a scroll-listener and translate... Well, they say it takes a genius to come up with simple solutions.. ;) I finished the project and it works perfectly without js, but I'll keep this in mind for the future.

Still, hats off to you for being awesome!
Answer: A simple jQuery plugin
This is a variation on Mahes' solution. You can call it like $('table#foo').scrollableTable();
The idea is:

Split the thead and tbody into separate table elements
Make their cell widths match again
Wrap the second table in a div.scrollable
Use CSS to make div.scrollable actually scroll

The CSS could be:
div.scrollable { height: 300px; overflow-y: scroll;}

Caveats

Obviously, splitting up these tables makes the markup less semantic. I'm not sure what effect this has on accessibility.
This plugin does not deal with footers, multiple headers, etc.
I've only tested it in Chrome version 20.

That said, it works for my purposes and you're free to take and modify it.
Here's the plugin:
jQuery.fn.scrollableTable = function () {
  var $newTable, $oldTable, $scrollableDiv, originalWidths;
  $oldTable = $(this);

  // Once the tables are split, their cell widths may change.
  // Grab these so we can make the two tables match again.
  originalWidths = $oldTable.find('tr:first td').map(function() {
    return $(this).width();
  });

  $newTable = $oldTable.clone();
  $oldTable.find('tbody').remove();
  $newTable.find('thead').remove();

  $.each([$oldTable, $newTable], function(index, $table) {
    $table.find('tr:first td').each(function(i) {
      $(this).width(originalWidths[i]);
    });
  });

  $scrollableDiv = $('
').addClass('scrollable');
  $newTable.insertAfter($oldTable).wrap($scrollableDiv);
};

Answer: :)
Not-so-clean, but pure HTML/CSS solution.
table {
    overflow-x:scroll;
}

tbody {
    max-height: /*your desired max height*/
    overflow-y:scroll;
    display:block;
}

Updated for IE8+
JSFiddle example
Comment: Good solution, only to mention, that these cells are floated and so according to content can have a different heights, it's visible if you set them borders: http://jsfiddle.net/ZdeEH/15/
Answer: Somehow I ended up with Position:Sticky working fine on my case:

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

th{
    position: sticky;
    top: 0px;
    border: 1px solid black;
    background: #ff5722;
    color: #f5f5f5;
    font-weight: 600;
}
td{
    background: #d3d3d3;
    border: 1px solid black;
    color: #f5f5f5;
    font-weight: 600;
}

div{
  height: 150px
  overflow: auto;
  width: 100%
}

                header 1
                header 2
                header 3
                header 4
                header 5
                header 6
                header 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

                data 1
                data 2
                data 3
                data 4
                data 5
                data 6
                data 7

Comment: This is the cleanest solution I seen so far. [caniuse](https://caniuse.com/#feat=css-sticky) shows that as of 5/2/2020, unprefixed position:sticky enjoys 90.06% global support. So this solution works well in all modern browsers.
Answer: Support for fixed footer
I extended Nathan's function to also support a fixed footer and maximum height.
Also, the function will set the CSS itself, and you only have to support a width.
Usage:
Fixed height:
$('table').scrollableTable({ height: 100 });

Maximum height (if the browser supports the CSS 'max-height' option):
$('table').scrollableTable({ maxHeight: 100 });

Script:
jQuery.fn.scrollableTable = function(options) {

    var $originalTable, $headTable, $bodyTable, $footTable, $scrollableDiv, originalWidths;

    // Prepare the separate parts of the table
    $originalTable = $(this);
    $headTable = $originalTable.clone();

    $headTable.find('tbody').remove();
    $headTable.find('tfoot').remove();

    $bodyTable = $originalTable.clone();
    $bodyTable.find('thead').remove();
    $bodyTable.find('tfoot').remove();

    $footTable = $originalTable.clone();
    $footTable.find('thead').remove();
    $footTable.find('tbody').remove();

    // Grab the original column widths and set them in the separate tables
    originalWidths = $originalTable.find('tr:first td').map(function() {
        return $(this).width();
    });

    $.each([$headTable, $bodyTable, $footTable], function(index, $table) {
        $table.find('tr:first td').each(function(i) {
            $(this).width(originalWidths[i]);
        });
    });

    // The div that makes the body table scroll
    $scrollableDiv = $('
').css({
        'overflow-y': 'scroll'
    });

    if(options.height) {
        $scrollableDiv.css({'height': options.height});
    }
    else if(options.maxHeight) {
        $scrollableDiv.css({'max-height': options.maxHeight});
    }

    // Add the new separate tables and remove the original one
    $headTable.insertAfter($originalTable);
    $bodyTable.insertAfter($headTable);
    $footTable.insertAfter($bodyTable);
    $bodyTable.wrap($scrollableDiv);
    $originalTable.remove();
};

Answer: Two divs, one for header, one for data.  Make the data div scrollable, and use JavaScript to set the width of the columns in the header to be the same as the widths in the data.  I think the data columns widths need to be fixed rather than dynamic.
Comment: re accessability, maybe we can to replace use of divs with styling on  and  ??
Answer: I realize the question allows JavaScript, but here is a pure CSS solution I worked up that also allows for the table to expand horizontally. It was tested with Internet Explorer 10 and the latest Chrome and Firefox browsers. A link to jsFiddle is at the bottom.
The HTML:
Putting some text here to differentiate between the header
aligning with the top of the screen and the header aligning
with the top of one of its ancestor containers.

        <colgroup>
            <col class="col1"></col>
            <col class="col2"></col>
        </colgroup>

Header 1

Header 2

Cell 1.1	Cell 1.2

Cell 2.1	Cell 2.2

Cell 3.1	Cell 3.2

Cell 4.1	Cell 4.2

Cell 5.1	Cell 5.2

Cell 6.1	Cell 6.2

Cell 7.1	Cell 7.2

And the CSS:
table{
    border-collapse: collapse;
    table-layout: fixed;
    width: 100%;
}
/* Not required, just helps with alignment for this example */
td, th{
    padding: 0;
    margin: 0;
}

tbody{
    background-color: #ddf;
}

thead {
    /* Keeps the header in place. Don't forget top: 0 */
    position: absolute;
    top: 0;
    background-color: #ddd;

    /* The 17px is to adjust for the scrollbar width.
     * This is a new css value that makes this pure
     * css example possible */
    width: calc(100% - 17px);
    height: 20px;
}

/* Positioning container. Required to position the
 * header since the header uses position:absolute
 * (otherwise it would position at the top of the screen) */
#positioning-container{
    position: relative;
}

/* A container to set the scroll-bar and
 * includes padding to move the table contents
 * down below the header (padding = header height) */
#scroll-container{
    overflow-y: auto;
    padding-top: 20px;
    height: 100px;
}
.header-col1{
    background-color: red;
}

/* Fixed-width header columns need a div to set their width */
.header-col1 div{
    width: 100px;
}

/* Expandable columns need a width set on the th tag */
.header-col2{
    width: 100%;
}
.col1 {
    width: 100px;
}
.col2{
    width: 100%;
}

http://jsfiddle.net/HNHRv/3/
Answer: I found this workaround - move header row in a table above table with data:

 <title>Fixed header</title>

 header 1
 header 2
 header 3

 row 1 col 1
 row 1 col 2
 row 1 col 3

 row 2 col 1
 row 2 col 2
 row 2 col 3

 row 3 col 1
 row 3 col 2
 row 3 col 3

 row 4 col 1
 row 4 col 2
 row 4 col 3

 row 5 col 1
 row 5 col 2
 row 5 col 3

 row 6 col 1
 row 6 col 2
 row 6 col 3

Comment: works for small tables, but if you have horizontal scrolling this solution will not work.
Comment: It will also not work properly since the table columns will not align. Here you are forcing width for td but we must not do...
Answer: For those who tried the nice solution given by Maximilian Hils, and did not succeed to get it to work with Internet Explorer, I had the same problem (Internet Explorer 11) and found out what was the problem.
In Internet Explorer 11 the style transform (at least with translate) does not work on
. I solved this by instead applying the style to all the  in a loop. That worked. My JavaScript code looks like this:
document.getElementById('pnlGridWrap').addEventListener("scroll", function () {
  var translate = "translate(0," + this.scrollTop + "px)";
  var myElements = this.querySelectorAll("th");
  for (var i = 0; i < myElements.length; i++) {
    myElements[i].style.transform=translate;
  }
});

In my case the table was a GridView in ASP.NET. First I thought it was because it had no
, but even when I forced it to have one, it did not work. Then I found out what I wrote above.
It is a very nice and simple solution. On Chrome it is perfect, on Firefox a bit jerky, and on Internet Explorer even more jerky. But all in all a good solution.
Answer: Very late to the party, but as it's still a party, here's my two cents using tailwindcss:

          Timestamp
          Species

          2022-02-09T08:20:39.967Z
          willow

          2022-02-09T08:21:29.453Z
          red osier dogwood

          2022-02-09T08:22:18.984Z
          buttonbush

Here's a full working example on tailwind's playgroud.
Answer: Use the latest version of jQuery, and include the following JavaScript code.
$(window).scroll(function(){
  $("id of the div element").offset({top:$(window).scrollTop()});
});

Comment: This doesn't seem to work. Maybe you could clarify what you're wanting us to do?
Comment: What div?  We're talking about tables here
Answer: This is not an exact solution to the fixed header row, but I have created a rather ingenious method of repeating the header row throughout the long table, yet still keeping the ability to sort.
This neat little option requires the jQuery tablesorter plugin. Here's how it works:
HTML

            Job Number
            Pri
            CLLI
            Market
            Job Status
            Technology
            MEI
            TEO Writer
            Quote Due
            Engineer
            ML Due
            ML Complete
            SPEC Due
            SPEC Complete
            Install Supervisor
            MasTec OJD
            Install Start
            Install Hours
            Revised CRCD
            Latest Ship-To-Site
            Total Parts
            OEM Rcvd
            Minor Rcvd
            Total Received
            % On Site
            Actions

                FL-8-RG9UP
                M
                FTLDFLOV
                SFL
                NOI
                TRANSPORT

                Chris Byrd
                Apr 13, 2013
                Kris Hall
                May 20, 2013
                May 20, 2013
                Jun 5, 2013
                Jun 7, 2013
                Joseph Fitz
                Jun 10, 2013
                TBD
                123
                <input readonly="true" name="revised_crcd" value="Jul 26, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058616">
                TBD
                N/A
                N/A
                N/A
                N/A
                N/A

                MTS13009SF
                M
                OJUSFLTL
                SFL
                NOI
                TRANSPORT

                DeMarcus Stewart
                May 22, 2013
                Ryan Alsobrook
                Jun 19, 2013
                Jun 27, 2013
                Jun 19, 2013
                Jul 4, 2013
                Randy Williams
                Jun 21, 2013
                TBD
                95
                <input readonly="true" name="revised_crcd" value="Aug 9, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058632">	TBD
                0
                0.00%
                0.00%
                0.00%
                0.00%
                <input style="float:left;" type="hidden" name="req_ship" class="reqShip hasDatepicker" id="dp1377194058464">

            .
            .
            .
            .

                Job Number
                Pri
                CLLI
                Market
                Job Status
                Technology
                MEI
                TEO Writer
                Quote Due
                Engineer
                ML Due
                ML Complete
                SPEC Due
                SPEC Complete
                Install Supervisor
                MasTec OJD
                Install Start
                Install Hours
                Revised CRCD
                Latest Ship-To-Site
                Total Parts
                OEM Rcvd
                Minor Rcvd
                Total Received
                % On Site
                Actions

Obviously, my table has many more rows than this. 193 to be exact, but you can see where the header row repeats. The repeating header row is set up by this function:
jQuery
// Clone the original header row and add the "repeated-header" class
var tblHeader = $('tr.boxheadrow').clone().addClass('repeated-header');

// Add the cloned header with the new class every 34th row (or as you see fit)
$('tbody tr:odd:nth-of-type(17n)').after(tblHeader);

// On the 'sortStart' routine, remove all the inserted header rows
$('#pmtable').bind('sortStart', function() {
    $('.repeated-header').remove();
    // On the 'sortEnd' routine, add back all the header row lines.
}).bind('sortEnd', function() {
    $('tbody tr:odd:nth-of-type(17n)').after(tblHeader);
});

Answer: I wish I had found @Mark's solution earlier, but I went and wrote my own before I saw this SO question...
Mine is a very lightweight jQuery plugin that supports fixed header, footer, column spanning (colspan), resizing, horizontal scrolling, and an optional number of rows to display before scrolling starts.
jQuery.scrollTableBody (GitHub)
As long as you have a table with proper
,
, and (optional)
, all you need to do is this:
$('table').scrollTableBody();

Answer: I developed a simple light-weight jQuery plug-in for converting a well formatted HTML table to a scrollable table with fixed table header and columns.
The plugin works well to match pixel-to-pixel positioning the fixed section with the scrollable section. Additionally, you could also freeze the number of columns that will be always in view when scrolling horizontally.
Demo & Documentation: http://meetselva.github.io/fixed-table-rows-cols/
GitHub repository: https://github.com/meetselva/fixed-table-rows-cols
Below is the usage for a simple table with a fixed header,
$(
).fxdHdrCol({
    width:     "100%",
    height:    200,
    colModal: [{width: 30, align: 'center'},
               {width: 70, align: 'center'},
               {width: 200, align: 'left'},
               {width: 100, align: 'center'},
               {width: 70, align: 'center'},
               {width: 250, align: 'center'}
              ]
});

Comment: What is *"a well HTML table"*?
Comment: @PeterMortensen Should have been "well formatted HTML". Edited, Thanks.
Answer: A lot of people seem to be looking for this answer. I found it buried in an answer to another question here: Syncing column width of between tables in two different frames, etc
Of the dozens of methods I have tried this is the only method I found that works reliably to allow you to have a scrolling bottom table with the header table having the same widths.
Here is how I did it, first I improved upon the jsfiddle above to create this function, which works on both td and th (in case that trips up others who use th for styling of their header rows).
var setHeaderTableWidth= function (headertableid,basetableid) {
            $("#"+headertableid).width($("#"+basetableid).width());
            $("#"+headertableid+" tr th").each(function (i) {
                $(this).width($($("#"+basetableid+" tr:first td")[i]).width());
            });
            $("#" + headertableid + " tr td").each(function (i) {
                $(this).width($($("#" + basetableid + " tr:first td")[i]).width());
            });
        }

Next, you need to create two tables, NOTE the header table should have an extra TD to leave room in the top table for the scrollbar, like this:

header1	header2	header3	header4	header5	header6

                    testdata
                    2
                    3
                    4
                    55555555555555
                    test

Then do something like:
        setHeaderTableWidth('headertable1', 'basetable1');
        $(window).resize(function () {
            setHeaderTableWidth('headertable1', 'basetable1');
        });

This is the only solution that I found on Stack Overflow that works out of many similar questions that have been posted, that works in all my cases.
For example, I tried the jQuery stickytables plugin which does not work with durandal, and the Google Code project here https://code.google.com/p/js-scroll-table-header/issues/detail?id=2
Other solutions involving cloning the tables, have poor performance, or suck and don't work in all cases.
There is no need for these overly complex solutions. Just make two tables like the examples below and call setHeaderTableWidth function like described here and boom, you are done.
If this does not work for you, you probably were playing with your CSS box-sizing property and you need to set it correctly. It is easy to screw up your CSS content by accident. There are many things that can go wrong, so just be aware/careful of that. This approach works for me.
Answer: Here's a solution that we ended up working with (in order to deal with some edge cases and older versions of Internet Explorer, we eventually also faded out the title bar on scroll then faded it back in when scrolling ends, but in Firefox and WebKit browsers this solution just works. It assumes border-collapse: collapse.
The key to this solution is that once you apply border-collapse, CSS transforms work on the header, so it's just a matter of intercepting scroll events and setting the transform correctly. You don't need to duplicate anything. Short of this behavior being implemented properly in the browser, it's hard to imagine a more light-weight solution.
JSFiddle: http://jsfiddle.net/podperson/tH9VU/2/
It's implemented as a simple jQuery plugin. You simply make your thead's sticky with a call like $('thead').sticky(), and they'll hang around. It works for multiple tables on a page and head sections halfway down big tables.
$.fn.sticky = function(){
    $(this).each( function(){
        var thead = $(this),
            tbody = thead.next('tbody');

        updateHeaderPosition();

        function updateHeaderPosition(){
            if(
                thead.offset().top < $(document).scrollTop()
                && tbody.offset().top + tbody.height() > $(document).scrollTop()
            ){
                var tr = tbody.find('tr').last(),
                    y = tr.offset().top - thead.height() < $(document).scrollTop()
                        ? tr.offset().top - thead.height() - thead.offset().top
                        : $(document).scrollTop() - thead.offset().top;

                thead.find('th').css({
                    'z-index': 100,
                    'transform': 'translateY(' + y + 'px)',
                    '-webkit-transform': 'translateY(' + y + 'px)'
                });
            } else {
                thead.find('th').css({
                    'transform': 'none',
                    '-webkit-transform': 'none'
                });
            }
        }

        // See http://www.quirksmode.org/dom/events/scroll.html
        $(window).on('scroll', updateHeaderPosition);
    });
}

$('thead').sticky();

Comment: Add `border: 2px solid red;` to `th`, scroll, and you will see the problem. I came up with this more basic solution myself: https://jsfiddle.net/x6pLcor9/19/
Comment: good solution but how do you include column borders between columns ( both in fixed header, aligned to the td data) ?
Comment: I'm not sure I understand your problem. border-collapse doesn't prevent you from using borders, margins, etc., it just removes the voodoo table metrics of yore.
Comment: Add the same dimensioned border to td and there's no problem. I don't see your point. Your version is much cleaner and doesn't use jQuery so I'd definitely go with something more like that today. (Although, frankly, I don't think I'd use a table at all today.)
Answer: By applying the StickyTableHeaders jQuery plugin to the table, the column headers will stick to the top of the viewport as you scroll down.
Example:

$(function () {
    $("table").stickyTableHeaders();
});

/*! Copyright (c) 2011 by Jonas Mosbech - https://github.com/jmosbech/StickyTableHeaders
 MIT license info: https://github.com/jmosbech/StickyTableHeaders/blob/master/license.txt */

;
(function ($, window, undefined) {
    'use strict';

    var name = 'stickyTableHeaders',
        id = 0,
        defaults = {
            fixedOffset: 0,
            leftOffset: 0,
            marginTop: 0,
            scrollableArea: window
        };

    function Plugin(el, options) {
        // To avoid scope issues, use 'base' instead of 'this'
        // to reference this class from internal events and functions.
        var base = this;

        // Access to jQuery and DOM versions of element
        base.$el = $(el);
        base.el = el;
        base.id = id++;
        base.$window = $(window);
        base.$document = $(document);

        // Listen for destroyed, call teardown
        base.$el.bind('destroyed',
        $.proxy(base.teardown, base));

        // Cache DOM refs for performance reasons
        base.$clonedHeader = null;
        base.$originalHeader = null;

        // Keep track of state
        base.isSticky = false;
        base.hasBeenSticky = false;
        base.leftOffset = null;
        base.topOffset = null;

        base.init = function () {
            base.$el.each(function () {
                var $this = $(this);

                // remove padding on
 to fix issue #7
                $this.css('padding', 0);

                base.$originalHeader = $('thead:first', this);
                base.$clonedHeader = base.$originalHeader.clone();
                $this.trigger('clonedHeader.' + name, [base.$clonedHeader]);

                base.$clonedHeader.addClass('tableFloatingHeader');
                base.$clonedHeader.css('display', 'none');

                base.$originalHeader.addClass('tableFloatingHeaderOriginal');

                base.$originalHeader.after(base.$clonedHeader);

                base.$printStyle = $('');
                $('head').append(base.$printStyle);
            });

            base.setOptions(options);
            base.updateWidth();
            base.toggleHeaders();
            base.bind();
        };

        base.destroy = function () {
            base.$el.unbind('destroyed', base.teardown);
            base.teardown();
        };

        base.teardown = function () {
            if (base.isSticky) {
                base.$originalHeader.css('position', 'static');
            }
            $.removeData(base.el, 'plugin_' + name);
            base.unbind();

            base.$clonedHeader.remove();
            base.$originalHeader.removeClass('tableFloatingHeaderOriginal');
            base.$originalHeader.css('visibility', 'visible');
            base.$printStyle.remove();

            base.el = null;
            base.$el = null;
        };

        base.bind = function () {
            base.$scrollableArea.on('scroll.' + name, base.toggleHeaders);
            if (!base.isWindowScrolling) {
                base.$window.on('scroll.' + name + base.id, base.setPositionValues);
                base.$window.on('resize.' + name + base.id, base.toggleHeaders);
            }
            base.$scrollableArea.on('resize.' + name, base.toggleHeaders);
            base.$scrollableArea.on('resize.' + name, base.updateWidth);
        };

        base.unbind = function () {
            // unbind window events by specifying handle so we don't remove too much
            base.$scrollableArea.off('.' + name, base.toggleHeaders);
            if (!base.isWindowScrolling) {
                base.$window.off('.' + name + base.id, base.setPositionValues);
                base.$window.off('.' + name + base.id, base.toggleHeaders);
            }
            base.$scrollableArea.off('.' + name, base.updateWidth);
        };

        base.toggleHeaders = function () {
            if (base.$el) {
                base.$el.each(function () {
                    var $this = $(this),
                        newLeft,
                        newTopOffset = base.isWindowScrolling ? (
                        isNaN(base.options.fixedOffset) ? base.options.fixedOffset.outerHeight() : base.options.fixedOffset) : base.$scrollableArea.offset().top + (!isNaN(base.options.fixedOffset) ? base.options.fixedOffset : 0),
                        offset = $this.offset(),

                        scrollTop = base.$scrollableArea.scrollTop() + newTopOffset,
                        scrollLeft = base.$scrollableArea.scrollLeft(),

                        scrolledPastTop = base.isWindowScrolling ? scrollTop > offset.top : newTopOffset > offset.top,
                        notScrolledPastBottom = (base.isWindowScrolling ? scrollTop : 0) < (offset.top + $this.height() - base.$clonedHeader.height() - (base.isWindowScrolling ? 0 : newTopOffset));

                    if (scrolledPastTop && notScrolledPastBottom) {
                        newLeft = offset.left - scrollLeft + base.options.leftOffset;
                        base.$originalHeader.css({
                            'position': 'fixed',
                                'margin-top': base.options.marginTop,
                                'left': newLeft,
                                'z-index': 3 // #18: opacity bug
                        });
                        base.leftOffset = newLeft;
                        base.topOffset = newTopOffset;
                        base.$clonedHeader.css('display', '');
                        if (!base.isSticky) {
                            base.isSticky = true;
                            // make sure the width is correct: the user might have resized the browser while in static mode
                            base.updateWidth();
                        }
                        base.setPositionValues();
                    } else if (base.isSticky) {
                        base.$originalHeader.css('position', 'static');
                        base.$clonedHeader.css('display', 'none');
                        base.isSticky = false;
                        base.resetWidth($('td,th', base.$clonedHeader), $('td,th', base.$originalHeader));
                    }
                });
            }
        };

        base.setPositionValues = function () {
            var winScrollTop = base.$window.scrollTop(),
                winScrollLeft = base.$window.scrollLeft();
            if (!base.isSticky || winScrollTop < 0 || winScrollTop + base.$window.height() > base.$document.height() || winScrollLeft < 0 || winScrollLeft + base.$window.width() > base.$document.width()) {
                return;
            }
            base.$originalHeader.css({
                'top': base.topOffset - (base.isWindowScrolling ? 0 : winScrollTop),
                    'left': base.leftOffset - (base.isWindowScrolling ? 0 : winScrollLeft)
            });
        };

        base.updateWidth = function () {
            if (!base.isSticky) {
                return;
            }
            // Copy cell widths from clone
            if (!base.$originalHeaderCells) {
                base.$originalHeaderCells = $('th,td', base.$originalHeader);
            }
            if (!base.$clonedHeaderCells) {
                base.$clonedHeaderCells = $('th,td', base.$clonedHeader);
            }
            var cellWidths = base.getWidth(base.$clonedHeaderCells);
            base.setWidth(cellWidths, base.$clonedHeaderCells, base.$originalHeaderCells);

            // Copy row width from whole table
            base.$originalHeader.css('width', base.$clonedHeader.width());
        };

        base.getWidth = function ($clonedHeaders) {
            var widths = [];
            $clonedHeaders.each(function (index) {
                var width, $this = $(this);

                if ($this.css('box-sizing') === 'border-box') {
                    width = $this[0].getBoundingClientRect().width; // #39: border-box bug
                } else {
                    var $origTh = $('th', base.$originalHeader);
                    if ($origTh.css('border-collapse') === 'collapse') {
                        if (window.getComputedStyle) {
                            width = parseFloat(window.getComputedStyle(this, null).width);
                        } else {
                            // ie8 only
                            var leftPadding = parseFloat($this.css('padding-left'));
                            var rightPadding = parseFloat($this.css('padding-right'));
                            // Needs more investigation - this is assuming constant border around this cell and it's neighbours.
                            var border = parseFloat($this.css('border-width'));
                            width = $this.outerWidth() - leftPadding - rightPadding - border;
                        }
                    } else {
                        width = $this.width();
                    }
                }

                widths[index] = width;
            });
            return widths;
        };

        base.setWidth = function (widths, $clonedHeaders, $origHeaders) {
            $clonedHeaders.each(function (index) {
                var width = widths[index];
                $origHeaders.eq(index).css({
                    'min-width': width,
                        'max-width': width
                });
            });
        };

        base.resetWidth = function ($clonedHeaders, $origHeaders) {
            $clonedHeaders.each(function (index) {
                var $this = $(this);
                $origHeaders.eq(index).css({
                    'min-width': $this.css('min-width'),
                        'max-width': $this.css('max-width')
                });
            });
        };

        base.setOptions = function (options) {
            base.options = $.extend({}, defaults, options);
            base.$scrollableArea = $(base.options.scrollableArea);
            base.isWindowScrolling = base.$scrollableArea[0] === window;
        };

        base.updateOptions = function (options) {
            base.setOptions(options);
            // scrollableArea might have changed
            base.unbind();
            base.bind();
            base.updateWidth();
            base.toggleHeaders();
        };

        // Run initializer
        base.init();
    }

    // A plugin wrapper around the constructor,
    // preventing against multiple instantiations
    $.fn[name] = function (options) {
        return this.each(function () {
            var instance = $.data(this, 'plugin_' + name);
            if (instance) {
                if (typeof options === 'string') {
                    instance[options].apply(instance);
                } else {
                    instance.updateOptions(options);
                }
            } else if (options !== 'destroy') {
                $.data(this, 'plugin_' + name, new Plugin(this, options));
            }
        });
    };

})(jQuery, window);
body {
    margin: 0 auto;
    padding: 0 20px;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 11px;
    color: #555;
}
table {
    border: 0;
    padding: 0;
    margin: 0 0 20px 0;
    border-collapse: collapse;
}
th {
    padding: 5px;
    /* NOTE: th padding must be set explicitly in order to support IE */
    text-align: right;
    font-weight:bold;
    line-height: 2em;
    color: #FFF;
    background-color: #555;
}
tbody td {
    padding: 10px;
    line-height: 18px;
    border-top: 1px solid #E0E0E0;
}
tbody tr:nth-child(2n) {
    background-color: #F7F7F7;
}
tbody tr:hover {
    background-color: #EEEEEE;
}
td {
    text-align: right;
}
td:first-child, th:first-child {
    text-align: left;
}

some really really wide content goes here

            Companies listed on NASDAQ OMX Copenhagen.

            Full name
            CCY
            Last
            +/-
            %
            Bid
            Ask
            Volume
            Turnover

            A.P. Møller...
            DKK
            33,220.00
            760
            2.34
            33,140.00
            33,220.00
            594
            19,791,910

            A.P. Møller...
            DKK
            34,620.00
            640
            1.88
            34,620.00
            34,700.00
            9,954
            346,530,246

            Carlsberg A
            DKK
            380
            0
            0
            371
            391.5
            6
            2,280

            Carlsberg B
            DKK
            364.4
            8.6
            2.42
            363
            364.4
            636,267
            228,530,601

            Chr. Hansen...
            DKK
            114.5
            -1.6
            -1.38
            114.2
            114.5
            141,822
            16,311,454

            Coloplast B
            DKK
            809.5
            11
            1.38
            809
            809.5
            85,840
            69,363,301

            D/S Norden
            DKK
            155
            -1.5
            -0.96
            155
            155.1
            51,681
            8,037,225

            Danske Bank
            DKK
            69.05
            2.55
            3.83
            69.05
            69.2
            1,723,719
            115,348,068

            DSV
            DKK
            105.4
            0.2
            0.19
            105.2
            105.4
            674,873
            71,575,035

            FLSmidth & Co.
            DKK
            295.8
            -1.8
            -0.6
            295.1
            295.8
            341,263
            100,301,032

            G4S plc
            DKK
            22.53
            0.05
            0.22
            22.53
            22.57
            190,920
            4,338,150

            Jyske Bank
            DKK
            144.2
            1.4
            0.98
            142.8
            144.2
            78,163
            11,104,874

            Københavns ...
            DKK
            1,580.00
            -12
            -0.75
            1,590.00
            1,620.00
            82
            131,110

            Lundbeck
            DKK
            103.4
            -2.5
            -2.36
            103.4
            103.8
            157,162
            16,462,282

            Nordea Bank
            DKK
            43.22
            -0.06
            -0.14
            43.22
            43.25
            167,520
            7,310,143

            Novo Nordisk B
            DKK
            552.5
            -3.5
            -0.63
            550.5
            552.5
            843,533
            463,962,375

            Novozymes B
            DKK
            805.5
            5.5
            0.69
            805
            805.5
            152,188
            121,746,199

            Pandora
            DKK
            39.04
            0.94
            2.47
            38.8
            39.04
            350,965
            13,611,838

            Rockwool In...
            DKK
            492
            0
            0
            482
            492

            Rockwool In...
            DKK
            468
            12
            2.63
            465.2
            468
            9,885
            4,623,850

            Sydbank
            DKK
            95
            0.05
            0.05
            94.7
            95
            103,438
            9,802,899

            TDC
            DKK
            43.6
            0.13
            0.3
            43.5
            43.6
            845,110
            36,785,339

            Topdanmark
            DKK
            854
            13.5
            1.61
            854
            855
            38,679
            32,737,678

            Tryg
            DKK
            290.4
            0.3
            0.1
            290
            290.4
            94,587
            27,537,247

            Vestas Wind...
            DKK
            90.15
            -4.2
            -4.45
            90.1
            90.15
            1,317,313
            121,064,314

            William Dem...
            DKK
            417.6
            0.1
            0.02
            417
            417.6
            64,242
            26,859,554

lots of content down here...

Answer: Here is an improved answer to the one posted by Maximilian Hils.
This one works in Internet Explorer 11 with no flickering whatsoever:
var headerCells = tableWrap.querySelectorAll("thead td");
for (var i = 0; i < headerCells.length; i++) {
    var headerCell = headerCells[i];
    headerCell.style.backgroundColor = "silver";
}
var lastSTop = tableWrap.scrollTop;
tableWrap.addEventListener("scroll", function () {
    var stop = this.scrollTop;
    if (stop < lastSTop) {
        // Resetting the transform for the scrolling up to hide the headers
        for (var i = 0; i < headerCells.length; i++) {
            headerCells[i].style.transitionDelay = "0s";
            headerCells[i].style.transform = "";
        }
    }
    lastSTop = stop;
    var translate = "translate(0," + stop + "px)";
    for (var i = 0; i < headerCells.length; i++) {
        headerCells[i].style.transitionDelay = "0.25s";
        headerCells[i].style.transform = translate;
    }
});

Answer: I like Maximillian Hils' answer but I had a some issues:

the transform doesn't work in Edge or IE unless you apply it to the th
the header flickers during scrolling in Edge and IE
my table is loaded using ajax, so I wanted to attach to the window scroll event rather than the wrapper's scroll event

To get rid of the flicker, I use a timeout to wait until the user has finished scrolling, then I apply the transform - so the header is not visible during scrolling.
I have also written this using jQuery, one advantage of that being that jQuery should handle vendor-prefixes for you
    var isScrolling, lastTop, lastLeft, isLeftHidden, isTopHidden;

    //Scroll events don't bubble https://stackoverflow.com/a/19375645/150342
    //so can't use $(document).on("scroll", ".table-container-fixed", function (e) {
    document.addEventListener('scroll', function (event) {
        var $container = $(event.target);
        if (!$container.hasClass("table-container-fixed"))
            return;

        //transform needs to be applied to th for Edge and IE
        //in this example I am also fixing the leftmost column
        var $topLeftCell = $container.find('table:first > thead > tr > th:first');
        var $headerCells = $topLeftCell.siblings();
        var $columnCells = $container
           .find('table:first > tbody > tr > td:first-child, ' +
                 'table:first > tfoot > tr > td:first-child');

        //hide the cells while returning otherwise they show on top of the data
        if (!isLeftHidden) {
            var currentLeft = $container.scrollLeft();
            if (currentLeft < lastLeft) {
                //scrolling left
                isLeftHidden = true;
                $topLeftCell.css('visibility', 'hidden');
                $columnCells.css('visibility', 'hidden');
            }
            lastLeft = currentLeft;
        }

        if (!isTopHidden) {
            var currentTop = $container.scrollTop();
            if (currentTop < lastTop) {
                //scrolling up
                isTopHidden = true;
                $topLeftCell.css('visibility', 'hidden');
                $headerCells.css('visibility', 'hidden');
            }
            lastTop = currentTop;
        }

        // Using timeout to delay transform until user stops scrolling
        // Clear timeout while scrolling
        window.clearTimeout(isScrolling);

        // Set a timeout to run after scrolling ends
        isScrolling = setTimeout(function () {
            //move the table cells.
            var x = $container.scrollLeft();
            var y = $container.scrollTop();

            $topLeftCell.css('transform', 'translate(' + x + 'px, ' + y + 'px)');
            $headerCells.css('transform', 'translateY(' + y + 'px)');
            $columnCells.css('transform', 'translateX(' + x + 'px)');

            isTopHidden = isLeftHidden = false;
            $topLeftCell.css('visibility', 'inherit');
            $headerCells.css('visibility', 'inherit');
            $columnCells.css('visibility', 'inherit');
        }, 100);

    }, true);

The table is wrapped in a div with the class table-container-fixed.
.table-container-fixed{
    overflow: auto;
    height: 400px;
}

I set border-collapse to separate because otherwise we lose borders during translation, and I remove the border on the table to stop content appearing just above the cell where the border was during scrolling.
.table-container-fixed > table {
   border-collapse: separate;
   border:none;
}

I make the th background white to cover the cells underneath, and I add a border that matches the table border - which is styled using Bootstrap and scrolled out of view.
 .table-container-fixed > table > thead > tr > th {
        border-top: 1px solid #ddd !important;
        background-color: white;
        z-index: 10;
        position: relative;/*to make z-index work*/
    }

            .table-container-fixed > table > thead > tr > th:first-child {
                z-index: 20;
            }

.table-container-fixed > table > tbody > tr > td:first-child,
.table-container-fixed > table > tfoot > tr > td:first-child {
    background-color: white;
    z-index: 10;
    position: relative;
}

Answer:

    gfgfdgsfgfdgfds

    gfgfdgsfgfdgfds

    gfgfdgsfgfdgfds

    gfgfdgsfgfdgfds

    gfgfdgsfgfdgfds

    gfgfdgsfgfdgfds

            1	 2	3	 4	5	 6	7	 8

11111111111111111111111111111111111111111111111111111111	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

1	2	3	4	5555555	66666666666	77777777777	8888888888888888

Answer: Additional to @Daniel Waltrip answer. Table need to enclose with div position: relative in order to work with position:sticky . So I would like to post my sample code here.
CSS
/* Set table width/height as you want.*/
div.freeze-header {
  position: relative;
  max-height: 150px;
  max-width: 400px;
  overflow:auto;
}

/* Use position:sticky to freeze header on top*/
div.freeze-header > table > thead > tr > th {
  position: sticky;
  top: 0;
  background-color:yellow;
}

/* below is just table style decoration.*/
div.freeze-header > table {
  border-collapse: collapse;
}

div.freeze-header > table td {
  border: 1px solid black;
}

HTML

   other contents ...

   other contents ...

   other contents ...

            header 1
            header 2
            header 3
            header 4
            header 5
            header 6
            header 7
            header 8
            header 9
            header 10
            header 11
            header 12
            header 13
            header 14
            header 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

            data 1
            data 2
            data 3
            data 4
            data 5
            data 6
            data 7
            data 8
            data 9
            data 10
            data 11
            data 12
            data 13
            data 14
            data 15

Demo
Answer: Almost all modern browsers will support it!

// '.tbl-content' consumed little space for vertical scrollbar, scrollbar width depend on browser/os/platfrom. Here calculate the scollbar width .
$(window).on("load resize ", function() {
  var scrollWidth = $('.tbl-content').width() - $('.tbl-content table').width();
  $('.tbl-header').css({
    'padding-right': scrollWidth
  });
}).resize();
h1 {
  font-size: 30px;
  color: #fff;
  text-transform: uppercase;
  font-weight: 300;
  text-align: center;
  margin-bottom: 15px;
}

table {
  width: 100%;
  table-layout: fixed;
}

.tbl-header {
  background-color: rgba(255, 255, 255, 0.3);
}

.tbl-content {
  height: 300px;
  overflow-x: auto;
  margin-top: 0px;
  border: 1px solid rgba(255, 255, 255, 0.3);
}

th {
  padding: 20px 15px;
  text-align: left;
  font-weight: 500;
  font-size: 12px;
  color: #fff;
  text-transform: uppercase;
}

td {
  padding: 15px;
  text-align: left;
  vertical-align: middle;
  font-weight: 300;
  font-size: 12px;
  color: #fff;
  border-bottom: solid 1px rgba(255, 255, 255, 0.1);
}

/* demo styles */

@import url(https://fonts.googleapis.com/css?family=Roboto:400,500,300,700);
body {
  background: -webkit-linear-gradient(left, #25c481, #25b7c4);
  background: linear-gradient(to right, #25c481, #25b7c4);
  font-family: 'Roboto', sans-serif;
}

section {
  margin: 50px;
}

/* follow me template */

.made-with-love {
  margin-top: 40px;
  padding: 10px;
  clear: left;
  text-align: center;
  font-size: 10px;
  font-family: arial;
  color: #fff;
}

.made-with-love i {
  font-style: normal;
  color: #F50057;
  font-size: 14px;
  position: relative;
  top: 2px;
}

.made-with-love a {
  color: #fff;
  text-decoration: none;
}

.made-with-love a:hover {
  text-decoration: underline;
}

/* for custom scrollbar for webkit browser*/

::-webkit-scrollbar {
  width: 6px;
}

::-webkit-scrollbar-track {
  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}

::-webkit-scrollbar-thumb {
  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
}

Fixed Table header

          Code
          Company
          Price
          Change
          Change %

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

          AAC
          AUSTRALIAN COMPANY
          $1.38
          +2.01
          -0.36%

          AAD
          AUSENCO
          $2.38
          -0.01
          -1.36%

          AAX
          ADELAIDE
          $3.22
          +0.01
          +1.36%

          XXD
          ADITYA BIRLA
          $1.02
          -1.01
          +2.36%

HTML table with fixed headers?


How do I migrate to a existing taxonomy reference field?

Question: Given a source that has a field with the corresponding ID which outputs the id in the field special like the field nid gives back a node id.
I want to map it to a taxonomy term that is already existing on the new site and not migrated.
This is the process part of my custom yml file:
process:
  nid: nid
  vid: vid
  # there were some more fields here that I left out.
  # hardcode destination node type as publication
  type:
    plugin: default_value
    default_value: publication
  # custom fields
  field_special:
    plugin: static_map
    source: special
    map:
      1: 1
      2: 2
      3: 3

I have a field_special as taxonomy term reference field in the node I want to migrate to.
How can I map the source field to be used in the destination field which is a taxonomy term reference field?
I'm using Drupal 8.1.2 with migrate_plus migrate_tools migrate_drupal enabled.
Comment: Can you add snippets to sample data you are using?
Comment: And are you using Drupal 8.1 or 8.0 (or 8.2)?
Comment: Also, are you using the migrate_plus module?
Answer: I've rewritten my yml to use:
field_special:
  plugin: iterator
  source: field_special
  process:
    target_id: tid

And written a preparerow function in my class to output an array as source property:
  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row) {
    $nid = $row->getSourceProperty('nid');

    $result = $this->getDatabase()->query(
      'SELECT
        GROUP_CONCAT(field_special_tid) as tids
      FROM
        {field_data_field_special} fs
      WHERE
        fs.entity_id = :nid
      ',
      array(':nid' => $nid)
    );
    foreach ($result as $record) {
      if (!is_null($record->tids)) {
        $row->setSourceProperty('special', explode(',', $record->tids));
      }
    }
    return parent::prepareRow($row);
  }

This workaround will do for now, but if you have a better answer please post so I can accept your answer.
Comment: Your solution seems the way to go. The only issue is the format in which you queried the database.
Comment: The query works, Tough it could be nicer but since it's a migration script I didn't care to use a entity field query.


Why does an email within an SOA file have a . instead of a @

Question: When I check the SOA for cloudflare.com, I get,
cloudflare.com has SOA record ns3.cloudflare.com. dns.cloudflare.com. 2027217413 10000 2400 604800 300

it returns the email as dns.cloudflare.com and not firstname.lastname@example.com.
Why is this ?
Comment: I don't understand why I was voted down, is this not a legitimate question?
Comment: Is there a problem related to this you're trying to solve, or are you just curious?
Comment: I'm Just curious
Answer: First of all, DNS is a binary protocol with distinct data types for each field, it's not free-form text.
The SOA RNAME field (which holds something that is supposed to represent an email address), in terms of its data type, is a domain-name.
The domain-name type is not a perfect match for an email address, but presumably the one-off needs for SOA RNAME field did not warrant an extra data type to be introduced.
A domain-name in DNS consist of a sequence of labels, which in text format are separated by dots (like you are used to see).
Without introducing some new type, if you were to write eg email@example.com., there would be one label dns@cloudflare, one com and the empty (root) label at the end.
While this would look reasonable in text format, it would also be strange (just in a different way), with how two distinctly different parts of the email address are now unnaturally fused together and only the text representation makes sense.
Another option could have been to use a character-string (like in eg TXT), but then it'd be completely unstructured and could have any kind of string value.
I think the solution we have is probably the least strange approach (at least from a technical perspective) to how to represent an email address without a adding a specialized data type for this purpose.


como tirar um unico texto de uma linha especifica num arquivo txt python

Question: Estou fazendo um mini sistema para cadastrar produtos e alterar suas quantidades, mas estou lidando com um problema de apagar em uma certa linha uma parte do texto, por exemplo: '00000 1' para '00000 4'(código do produto e sua quantidade!).
Irei deixar o código com uma linha marcada para entenderem melhor.
from random import randint

while True:
    print('1 - ver lista de produtos\n2 - alterar quantidade\n3 - adicionar produto')
    opcao = int(input('opção: '))
    if opcao == 1:
        with open('NAME', 'r', encoding="utf8") as f:
            for i in f:
                print(i)

    elif opcao == 2:
        produto = input('qual o codigo do produto?\n>')
        with open('NAME', 'rt+', encoding="utf8") as f:
            for i in f:
                if i[:5] == produto:
                    print(f'a quantidade do produto é: {i[6:]}')
                    qtd = str(input('para quantos deseja alterar?\n>'))

                    # é aqui onde eu preciso colocar o código mas não sei como faço ele

                else:
                    continue

    elif opcao == 3:
        a = randint(10000, 99999)
        print(f'o código do produto é: {a}')
        with open('NAME', 'r', encoding="utf8") as f:
            for i in f:
                if i[:5] == a:
                    a = randint(10000, 99999)
                else:
                    pass
            quantidade = int(input('qual a quantidade do produto?\n>'))
        open('NAME', 'at+').write(f'{a} ')
        open('NAME', 'at+').write(f'{quantidade}')
        open('NAME', 'at+').write('\n')

    else:
        pass

Answer: Solução possível abaixo:
from random import randint

while True:
    print('1 - ver lista de produtos\n2 - alterar quantidade\n3 - adicionar produto')
    opcao = int(input('opção: '))
    if opcao == 1:
        with open('NAME', 'r', encoding="utf8") as f:
            for i in f:
                print(i)

    elif opcao == 2:
        produto = input('qual o codigo do produto?\n>')
        with open('NAME', 'rt+', encoding="utf8") as f:
            # Lê todas as linhas do arquivo de antemão
            linhas = f.readlines()
            for indice, linha in enumerate(linhas):
                if linha[:5] == produto:
                    print(f'a quantidade do produto é: {linha[6:]}')
                    qtd = str(input('para quantos deseja alterar?\n>'))

                    # Apenas linha = f'{linha[:5]} {qtd}\n' não modificaria a lista
                    linhas[indice] = f'{linha[:5]} {qtd}\n'

                else:
                    continue
            # Reposiciona o ponteiro do arquivo pro início dele. Caso contrário estaria escrevendo as linhas ao final dele
            f.seek(0)
            f.writelines(linhas)

    elif opcao == 3:
        a = randint(10000, 99999)
        print(f'o código do produto é: {a}')
        with open('NAME', 'r', encoding="utf8") as f:
            for i in f:
                if i[:5] == a:
                    a = randint(10000, 99999)
                else:
                    pass
            quantidade = int(input('qual a quantidade do produto?\n>'))
        open('NAME', 'at+').write(f'{a} ')
        open('NAME', 'at+').write(f'{quantidade}')
        open('NAME', 'at+').write('\n')

    else:
        pass

Não é a solução mais rápida, pois escreve o arquivo inteiro novamente.
Comment: Muito obrigado cara você me ajudou muito, o código funciona sim, eu tinha pensado nisso mas não fazia ideia de como fazer então acabei desistindo dessa opção, mas obrigado!


Proving a Regular Expression

Question: $L$ is a language from the alphabet $\Sigma  = \{a,b \}$.
Define $C(L)$ as another language. This language produces a $w$ as an element of $\{a,b\}^*$ with the property that there exists a $v \in L$ such that the number of $a$'s in $w$ is equivalent to the number of $a$'s in $v$.
I wanted to prove that if $L$ is regular, then $C(L)$ is also regular.
Note that there we are not given what L is, so the proof cannot make use of converting L into a DFA for example.
I thought of using homomorphism $h$ to change $L$ into a language that is equivalent to $C(L)$ and using the closure property that if $L$ is regular then $h(L)$ is also regular. Since $h(L)$ is equivalent to $C(L)$, we have proven the conditional.
Am I on the right track? Thanks.
Answer: Given a word $u$, let $|u|_a$ denote the number of occurrences of $a$ in $u$. If I understood correctly, your language $C(L)$ is defined as follows:
$$
  C(L) = \{w \in \Sigma^* \mid \text{there exists $v \in L$ such that $|w|_a = |v|_a$}\}
$$
Let $h: \Sigma^* \to a^*$ be the homomorphism defined by $h(w) = a^{|w|_a}$. I claim that $C(L) = h^{-1}(h(L))$. Indeed, $w \in h^{-1}(h(L))$ if and only if
there exists $v \in L$ such that $h(w) = h(v)$, that is $|w|_a = |v|_a$.
Since regular languages are closed under homomorphisms and under inverses of homomorphisms, the language $C(L)$ is regular.
Remark. The same proof would show that if $L$ is context-free, then $C(L)$ is context-free.
Answer: I don't see how you're going to construct such a homomorphism. I would start with a DFA $M$ for $L$ and modify it to get an NFA for $C(L)$: add a $b$-transition from each state of $M$ to itself, and wherever $M$ has a transition
$$q_1\overset{b}\longrightarrow q_2\;,$$
add a transition
$$q_1\overset{\epsilon}\longrightarrow q_2\;.$$
Then show that the resulting NFA accepts $C(L)$.
Alternatively, you could start with a regular grammar $G$ for $L$ and add a new non-terminal symbol $B$. Replace each $b$ in the productions of $G$ with $B$, and add productions $B\to bB\mid\epsilon$. Then show that this modified grammar generates $C(L)$.
Comment: How can we do that if we're not given a language $L$ to make a DFA from?
Comment: @tommark: By proving that the modification that I described (either to a DFA or to a regular grammar) always works: you get precisely the words in $\{a,b\}^*$ that have the same number of $a$s as some word of $L$.

Proving a Regular Expression


Model selection criteria that represent a compromise between AIC and BIC

Question: I am very familiar with the ideas and formula of the two popular model selection criteria AIC/AICc and BIC. When I use them for practical problems in chemometrics, the use of AIC/AICc often gives models with too many undesirable variables selected, but BIC seems to choose two few variables. My question is whether there are any other criteria that are calculable as EASILY as AIC and BIC, but represent a middle ground between the two in terms of penalizing model complexity.  I know there are a wide range of other information criteria (e.g., DIC, GIC, and HIC). But what I look for is one that is almost as simple as AIC or BIC. For example, is there a AIC/BIC-like criterion that uses a different factor for the penalty term?
Answer: There is the Hannan–Quinn information criterion (HQIC):
$$
\text{HQ}=-2L_{\text{max}}+2k\ln(\ln(n))
$$
where $L_{\text{max}}$ is the maximized log-likelihood, $k$ is the number of parameters, and $n$ is the number of observations (the sample size). The penalty is smaller than BIC's $k\ln(n)$ but larger than AIC's $2k$. HQIC is consistent for model selection like BIC, and its penalty is the minimal possible penalty to achieve consistency. It is not efficient like AIC but not very far from that since $\ln(\ln(n))$ is not that much bigger than 1 (1.5 for $n=100$, 1.9 for $n=1000$, 2.2 for $n=10000$). According to Wikipedia

HQC is asymptotically very well-behaved. Van der Pas and Grünwald prove that model selection based on a modified Bayesian estimator, the so-called switch distribution, in many cases behaves asymptotically like HQC, while retaining the advantages of Bayesian methods such as the use of priors etc.

And obviously, HQIC is just as easy to calculate as AIC or BIC.
Comment: This is a great answer. Yes. exactly what I am looking for. Thanks a lot.
Comment: @sshwang, thank you, I am pleased to hear that.