Hatena::ブログ(Diary)

Cyokodog :: Diary

December 12, 2009

テーブルのヘッダとフッタを固定する簡易プラグイン

更新履歴

2010-06-02
jQuery.exTable.js を Ver 0.1.2 に更新
  • jQuery 1.3 以上のバージョンで正常動作しない不具合を修正しました。
  • サンプルを含む zip ファイルでダウンロードできるようにしました。
2010-01-21
jQuery.exTable.js を Ver 0.1.1 に更新
  • セルの内包要素の膨張、table 要素の margin 設定等によるレイアウト崩れ抑止処理を追加しました
  • ヘッダ・ボディ・フッタ内のテーブル要素を取得するゲッターメソッドを追加しました
  • 記事中のソースを修正しました

だいぶ前に書いた「tableのヘッダを固定させる簡易scriptをサクっと作ってみた」というエントリに、「サンプルソースを参考にフッタも固定しようとしたけどうまくいかない」というコメントを先日頂きました。今にして見ると、前回、前々回のエントリで書いた プラグインの定義パターンという意味ではあまり参考しないほうが良さげな実装だったので、フッタ固定機能もつけて再実装してみました。

適用したプラグインの定義パターン

まずプラグインの定義パターンとしては「prototype オブジェクトを使用した定義」を採用しました。(詳しくは「jQuery プラグインの定義パターンについて調べてみた」)

//2.7 実装を prototype オブジェクトで定義
(function($){
    $.myPlugin = function( elem , option ){
        // init
    }
    $.extend( $.myPlugin.prototype,{
        hoge : function(){ ... },
        fuga : function(){ ... }
    });
    $.fn.myPlugin = function( option ) {
        return this.each(function() {
            new $.myPlugin( this , option);
        });
    };
})(jQuery);

プラグイン API の提供方法は、コールバック関数経由で API オブジェクトを引き渡す方法にしました。(詳しくは「プラグイン API の定義パターンについて調べてみた」)

//2.3 コールバック関数経由の API オブジェクトの取得
$(element).myPlugin({ onHogeClick : function( api ){
    api.execHoge();
}});

実際の実装

jquery.extable.0.1.1.js

(function($){
    $.ex = $.ex || {};

    $.ex.table = function(idx , targets , option){
        var o = this,
        c = o.config = $.extend({} , $.ex.table.defaults , option);

        //処理対象となった要素を保持
        c.targets = targets;
        c.target = c.targets.eq(idx);
        c.index = idx;

        //新設するウィジェットの外枠を生成
        c.container = $('<div class="ex-table-container">' +
            '<div class="ex-table-head"><table/></div>' +
            '<div class="ex-table-body"></div>' +
            '<div class="ex-table-foot"><table/></div>' +
            '</div>');

        //ウィジェットのヘッダ、ボディ、フッタ枠の取得と幅調整
        c.head = c.container.find('> div.ex-table-head').css({
            'padding-right':c.scrollbarWidth
        });
        c.foot = c.container.find('> div.ex-table-foot').css({
            'padding-right':c.scrollbarWidth
        });
        c.headTable = c.head.find('> table');
        c.footTable = c.foot.find('> table');
        c.body = c.container.find('> div.ex-table-body').css({
            'overflow-x' : 'hidden',
            'overflow-y' : 'scroll',
            height : c.height
        });

        //処理対象テーブルの thead / tbody / tfoot の取得
        var thead = c.target.find('> thead')
            ,tfoot = c.target.find('> tfoot')
            ,tbody = c.target.find('> tbody');

        //幅を固定する
        o._fixedWidth(thead);
        o._fixedWidth(tfoot);
        o._fixedWidth(tbody);
        c.container.width(c.target.width() + c.scrollbarWidth + 1);
        
        //新設するウィジェットの外枠をページに挿入
        c.target.after(c.container);
        
        //ウィジェットのヘッダ、フッタ枠に thead と tfoot を挿入
        c.target.find('> thead').appendTo(c.head.find('> table'));
        c.target.find('> tfoot').appendTo(c.foot.find('> table'));
        c.target.appendTo(c.body);

        //テーブル要素の margin をクリア、table-layout でレイアウトを固定化
        $([c.target[0],c.headTable[0],c.footTable[0]]).css({
            'table-layout':'fixed',
            'margin':0
        });
    
        //初期化時のコールバック関数の実行
        if( c.onInit ){
            c.onInit.apply( c.targets , [ o ] );
        }
    }
    $.extend($.ex.table.prototype,{
        _fixedWidth : function(stack){
            var cols = stack.find('> tr:eq(0) > *');
            cols.each(function( idx ){
                var col = cols.eq(idx);
                col.width(col.width()).css('overflow','hidden');
            });
        },
        getIndex : function(){
            return this.config.index;
        },
        getTargets : function(){
            return this.config.targets;
        },
        getTarget: function(){
            return this.config.target;
        },
        getContainer : function(){
            return this.config.container;       
        },
        getHead : function(){
            return this.config.head;        
        },
        getHeadTable : function(){
            return this.config.headTable;       
        },
        getBody : function(){
            return this.config.body;        
        },
        getBodyTable: function(){
            return this.config.target;
        },
        getFoot : function(){
            return this.config.foot;        
        },
        getFootTable : function(){
            return this.config.footTable;       
        }
    });
    
    $.ex.table.defaults = {
        scrollbarWidth :16,
        height : 200,
        onInit : null
    }
    $.fn.exTable = function(option){
        var targets = this;
        return targets.each(function(idx){
            targets.eq(idx).data(
                'ex-table',
                new $.ex.table(idx,targets,option)
            );
        });
    }
})(jQuery);
使い方

固定したいヘッダ、フッタは thead / tfoot に記述し、データ部は tbody に記述します。

<table id="example">
    <thead><tr><th>...</tr></thead>
    <tbody>
        <tr><td>...</td></tr>
        <tr><td>...</td></tr>
        ....
    </tbody>
    <tfoot><tr><td>...</tr></tfoot>
</table>

以下のように実行します。

jQuery(function($){
    $('#example').exTable();
})  

f:id:cyokodog:20091213021219p:image

Demo

CSS を適用してみる

生成される HTML の構造は以下のようになります。

<div class="ex-table-container">
    <div class="ex-table-head">
        <table>
            <tr><th>...</tr>
        </table>
    </div>
    <div class="ex-table-body">
        <table>
            <tr><td>...</tr>
        </table>
    </div>
    <div class="ex-table-foot">
        <table>
            <tr><td>...</tr>
        </table>
    </div>
</div>

クラス名に CSS を当ててみます。

div.ex-table-container{
    background:#f0f0f0;
    border:solid 1px #d0d0d0;
}
div.ex-table-body td{
    background:#fff;
}

f:id:cyokodog:20091213021304p:image

Demo

初期化処理用コールバック関数 onInit にて、API オブジェクト経由で各要素を取得し、CSS を当てることもできます。

jQuery(function($){
    $('#example').exTable({
        onInit : function(api){
            api.getContainer().css({
                background:'#aaccff',
                border:'solid 1px #5588aa'
            });
            api.getBody().find('td').css({
                background:'#f0f0f0'
            });
        }
    });
});

f:id:cyokodog:20091213021332p:image

Demo

API オブジェクトは 、data() メソッドで任意のタイミングで取得することもできます。

var api = $('#example').data('ex-table');

テーブルの高さを指定する場合は、height パラメータを指定します。デフォルトは 200 になってます。

jQuery(function($){
    $('#example').exTable({
        height : 100
    });
});

プラグイン内で定義されてるのパラメータデフォルト値を変更する場合は $.ex.table.defaults を上書きします。

$.extend($.ex.table.defaults,{
    height : 100
});
ダウンロード

こちらからどうぞ。

karuakarua 2009/12/19 21:20 こんばんわ。
フッタの固定も出来るよう、新たに作っていただき、ありがとうございました。
早速使わせてもらったのですが、望んでいた通りの動作です!
まだJqueryについては初心者なのですが、サンプルはとても参考になりました。もっと勉強したいと思います。。

cyokodogcyokodog 2009/12/20 08:40 お役にたてて幸いです。
また何かありましたらコメントお願いします

naonao 2010/06/01 10:08 はじめまして。
スクリプトをお借りしましたが、jquery1.4.2で試したところtbodyのappendToがうまく動作していないようでした。jsファイルの54行目から57行目をそれぞれ1行ずつの構文に置き換えたらうまくいきました。
ご報告まで。

cyokodogcyokodog 2010/06/02 01:21 ありがとうございます。
試したところ、jQuery 1.3以上で上記のロジックを実行するとご指摘の append() メソッド返却値が undefined
になってしまうようです。(1.2.6だと問題なし)
記事とソースを修正いたしました。
また何かありましたらよろしくお願いします。

66 2010/07/22 04:41 はじめまして。
色々探していて辿りつき、試した中で一番理想に近いものだったので使わせていただいてます!
ひとつ希望があるのですが、
仮にheightのサイズを200に固定していた場合、
内容が少なくて200に満たない場合でも200の高さが確保されて
右側にはグレーアウトしたスクロールバーが出たままで足りない部分は空白になると思うのですが、
内容が固定サイズ以下の場合はスクロールバーは出ずに、
tableより下の要素がちゃんと詰まるようにできないでしょうか?
もし可能なら検討していただければとても助かります。。。
よろしくお願いします!

cyokodogcyokodog 2010/07/22 06:57 コメントありがとうございます。
記事の方はまだ修正してませんが、maxHeight パラメータっていうのを設けました。
こちらに高さを指定すればご希望の動作になるかと思います。
詳細はzipファイルをダウンロードしexample-1.3.0.html 〜 example-1.5.0.html
をご確認ください。

denden 2011/07/22 10:40 気が向いたら質問にご回答頂けると助かります。

テーブルヘッダ部にデータ部縦罫線に合わせた
縦の罫線を表示させたいと考えております。
jQuery.exTable.jsにて、ヘッダ部に上記のような
縦罫線を表示する指定はございますでしょうか?
(普通にヘッダ部に罫線を表示させたら、項目毎に
縦線が、少しずつデータ部側の線とずれまして。。。
私の認識違いであれば申し訳ございません。)

cyokodogcyokodog 2011/07/27 02:26 denさん

返信遅くてすいません。
github からファイル一式ダウンロードして、そのなかにあるexample-1.6.0.htmlを参考してみていただけますでしょうか

denden 2011/07/27 19:51 cyokodog 様
denです。

深夜のご対応ありがとうございます。

0.1.3と0.1.4の比較及び、動作の確認をさせて頂きました。
すばらしい出来栄えです。
又正直、驚愕しております。

改めて、勉強させて頂きます。

以上、ありがとうございました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証