jQueryでテーブルのヘッダーをスクロールしても上部に固定する

ユーザーがテーブルをスクロールして見えなくなったときに、ヘッダーがページの一番上に表示されるようなHTMLテーブルをデザインしたいと思っています。 例えば、テーブルはページから500ピクセル下にありますが、ユーザーがヘッダーをスクロールして視界から外した場合(ブラウザがウィンドウズ表示でなくなったことを何らかの方法で検出した場合)、ヘッダーが最上部に留まるようにするにはどうしたらよいでしょうか? どなたかJavascriptでの解決方法を教えてください。

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

上記の例では、<thead>が表示されなくなった場合、ページと一緒にスクロールさせたいと思います。

重要:<tbody>がスクロールバー(overflow:auto)を持つような解決策は 求めていません

ソリューション

windowscrollイベントハンドラを利用して、固定された位置にある別のtable`を使って、ページの一番上にヘッダーを表示する、というようなことをします。

HTML:

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

CSS:

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

JavaScript:

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

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

    if (offset >= tableOffset && $fixedHeader.is(":hidden")) {
        $fixedHeader.show();
    }
    else if (offset < tableOffset) {
        $fixedHeader.hide();
    }
});

これは、ユーザーがページを十分に下にスクロールして元のテーブルヘッドが隠れたときに、テーブルヘッドを表示します。 ユーザーがページを十分に上にスクロールすると、テーブルヘッドは再び非表示になります。

動作例: http://jsfiddle.net/andrewwhitaker/fj8wM/

解説 (21)

私は、固定サイズの列やテーブル全体の高さを固定することなく、同じ効果を得ようとしました。

私が思いついた解決策は、ハックです。これは、テーブル全体を複製し、ヘッダ以外のすべてを隠し、ヘッダを固定の位置にするというものです。

HTML

<div id="table-container">
<table id="maintable">
    <thead>
        <tr>
            <th>Col1</th>
            <th>Col2</th>
            <th>Col3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>some really long line here instead</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
                <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
                <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
    </tbody>
</table>
<div id="bottom_anchor"></div>
</div>
body { height: 1000px; }
thead{
    background-color:white;
}

javascript


function moveScroll(){
    var scroll = $(window).scrollTop();
    var anchor_top = $("#maintable").offset().top;
    var anchor_bottom = $("#bottom_anchor").offset().top;
    if (scroll>anchor_top && scroll
解説 (15)

列の幅を変更することで問題を解決することができました。 上のAndrew'さんの解決策(ありがとうございます!)から始めて、クローンされたtd'sの幅を設定するために小さなループを一つ追加しました。

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

これで、テーブル全体をクローンしてボディを隠さなくても問題が解決しました。 私はJavaScriptとjQueryの初心者なので(そしてstack overflowの初心者なので)、どんなコメントでも歓迎します。

解説 (1)