If you can fix the column widths - it's a lot easier. If you want the browser to figure out the widths, it gets a lot harder. Basically, have the table in div that scrolls (height, overflow:auto) and have that div inside a position:relative div. In the outer div, have another div position:absolute, overflow:hidden, height: whatever the header height is, set this div's innerHTML to the innerHTML of the inner div; Here is a page that demonstrates. There are lots of gotchas, but it's doable...
Note as you refresh and the data size changes, the header matches up perfectly. That's the real trick.