For what it's worth:
There are a couple of answers here but I don't think they actually answer the question. They don't operate on table headers (thead elements) or they use 3rd party libraries. There are some subtle issues with lifting a thead out of the table - the largest of which is that the cells will collapse down if the header text is wider or narrower than the data in the tbody.
Here is my solution that solves the problem without any libraries and working on table headers. It makes no assumptions about the styling of the table, or the size of the headers; everything is calculated. Only tested in Chrome per the requirements of the OP.
Script:
function initFloatingHeaders() {
var tables = document.querySelectorAll('table.float-header');
var i = tables.length;
while (i--) {
var table = tables[i];
var wrapper = document.createElement('div');
wrapper.className = 'floating-header';
var clone = table.cloneNode(true);
wrapper.appendChild(clone);
table.parentNode.insertBefore(wrapper, table);
var thead = table.querySelector('thead');
wrapper.style.width = thead.scrollWidth + 'px';
wrapper.style.height = thead.scrollHeight + 'px';
wrapper.style.left = table.offsetLeft + 'px';
}
window.addEventListener('scroll', function() {
var headers = document.querySelectorAll('div.floating-header');
var bodyHeight = document.body.offsetHeight;
var i = headers.length;
while (i--) {
var header = headers[i];
var tableBounds = header.nextSibling.getBoundingClientRect();
if (tableBounds.top < 0 && tableBounds.bottom > 0) {
header.style.display = 'block';
} else {
header.style.display = null;
}
}
}, false);
}
Tables should have the class float-header applied and initFloatingHeaders should be called on load or documentReady. Example: http://jsbin.com/ulusit/2 (Old example with bad transitions: http://jsbin.com/ulusit/)