もっと便利に!jQueryでラクラクサイト制作(実践サンプル付き)

第7回タブパネルを実装する(前編)

タブプラグインを作ってみよう

最近ではWebサイトを制作する際によく使われるのが、既に完成しているJavaScriptファイルを読み込み、実行するだけで簡単に利用できるプラグインやライブラリといったものです。

jQueryの場合、プラグインと呼ばれますが、そのプラグインだけでも、数え切れないくらい多くのものが公開されています。

代表的なものでは、jQueryオフィシャルのUIプラグインなどが挙げられます。この連載ではプラグインの紹介は行いませんが、第7回・8回では実際にプラグインを作成してみましょう。

今回の第7回では、タブパネルを実装するスクリプトを書くところまでを紹介し、次回の第8回は今回実装したタブパネルスクリプトを元にプラグイン化するところまでを紹介していきます。

タブパネルを実装するスクリプトを書いてみよう

タブパネルの実装について、考えてみましょう。タブパネルUIは様々なシーンで使われますが、タブパネルは見ての通りとても簡単なもので、タブ部分をクリックすることで、対応するパネルを表示する、というものです。

実装方法は様々ですが、本連載では、ul要素をタブグループ、その子要素のliをタブとして、さらにその子要素aが持つhref属性に#idという形でリンクを用意しておき、それ以下に存在するブロック要素に対応するidを付けて実装していくことにします。具体的には以下のHTMLをベースに実装していきます。

タブパネルを実装するにあたって、マークアップに若干のルールを作っておく必要があります。タブパネル全体をdiv.tabAreaで囲い、その中にタブを識別するためのul.tab、それぞれに対応するdiv#tab1, div#tab2などのようにidを付与する、ということです。

タブパネルベースHTML
<div class="tabArea">
    <ul class="tab">
        <li><a href="#tab1">tab1</a></li>
        <li><a href="#tab2">tab2</a></li>
    </ul>
    <div id="tab1" class="tabPanel">
        tab1tab1tab1tab1tab1tab1tab1<br />
        tab1tab1tab1tab1tab1tab1tab1<br />
        tab1tab1tab1tab1tab1tab1tab1<br />
        tab1tab1tab1tab1tab1tab1tab1<br />
        tab1tab1tab1tab1tab1tab1tab1<br />
        tab1tab1tab1tab1tab1tab1tab1
    </div>
    <div id="tab2" class="tabPanel">
        tab2tab2tab2tab2tab2tab2tab2<br />
        tab2tab2tab2tab2tab2tab2tab2<br />
        tab2tab2tab2tab2tab2tab2tab2<br />
        tab2tab2tab2tab2tab2tab2tab2<br />
        tab2tab2tab2tab2tab2tab2tab2<br />
        tab2tab2tab2tab2tab2tab2tab2
    </div>
</div>

タブパネルの初期化

順を追って見ていきましょう。

まずは、tabArea、tabPanel、tabをそれぞれ取得しておきます。

タブパネル実装 - JS - 1
var tabArea = $('div.tabArea');
var tabPanel = $('div.tabPanel',tabArea);
var tab = $('ul.tab li',tabArea);

次に、最初のタブパネル以外を非表示にします。タブパネルは先ほど取得しているので、⁠最初以外の」「非表示にする」を組み合わせるだけです。⁠~以外を」は.not(セレクタ)で絞れるので、⁠最初の」を選択する「:first」セレクタを用いて、⁠最初以外の」を取得します。⁠非表示にする」は.hide()をつけるだけです。

タブパネル実装 - JS - 2
tabPanel.not(':first').hide();

続いて、最初のタブパネルは選択された状態にするので、タブ自体も選択された状態にしておく必要があります。これはclass属性にactiveを指定することで、CSSを用いて選択されている状態を実現します。

タブも先ほど取得しているので、⁠最初の」「classにactiveを追加する」を組み合わせるだけです。⁠最初の」は.eq(0)で、⁠classにactiveを追加する」は.addClass('active')でそれぞれ行います。

タブパネル実装 - JS - 3
tab.eq(0).addClass('active');

ここまでで、タブパネルの初期化が終わりました。最初以外のタブパネルが非表示になり、最初のタブが選択された状態になっているはずです。サンプルを確認してみてください。

タブをクリックしたときの動作を追加する

タブをクリックしたときに、パネルが入れ替わるので、.click()を使います。

タブパネル実装 - JS - 4
tab.click(function(){ /* ここに処理を書いていく */ });

まずは、クリックされたタブに対応するパネルのidを取得します。タブのa要素のhrefには「#tabId」が、対象のタブパネルのidには「tabId」が入っている、というルールを決めたのを思い出してください。また$(セレクタ)はidを指定するときには$('#id')という形で指定できるので、クリックされたタブのa要素から「#id」部分を抜き出して、それをそのまま$()にあてはめれば対象のタブパネルを取得できます。⁠#id」を抜き出すには、.attr('hash')を使います。hashとはhref属性に含まれる「#~~を指します。

試しにconsoleで見てみるとよいでしょう。

consoleには「#tabId」が表示されているはずです。

タブパネル実装 - JS - 5
tab.click(function(){
    var targetTabId = $('a',this).attr('hash');
    console.log(targetTabId);
});

ここまでくればもう完成したようなもので、残っている処理は、

  • 現在表示されているタブパネルを非表示にして、
  • 選択状態のタブからactiveを取り除き
  • クリックされたタブにactiveを追加
  • 取得した「#tabId」のタブパネルを表示する

の4つです。

実際には、表示しているタブをわざわざ取得するよりも、全てのタブパネルを非表示にしてから、対象のタブパネルを表示した方が効率的ですし、同じく、選択状態のタブをわざわざ取得するよりも、タブ全体からactiveを取り除いて、クリックされたタブにactiveを追加する方が効率的です。例えば、.removeClass()は指定したclassが含まれない場合は、その処理を無視してくれるので、特に何も気にする必要はありません。

以上をふまえると、残っている処理は以下のように記述するだけです。

タブパネル実装 - JS - 6
tab.removeClass('active');
tabPanel.hide();
$(this).addClass('active');
$(targetTabId).show();

これらを先ほどのものと合わせて、最後にタブをクリックすると、子要素のa要素をクリックしたことにもなりますが、そうするとa要素のhref属性の挙動(リンク先に移動する挙動)とかぶってしまうので、return falseを追加することで、クリックイベントがa要素に伝播するのを防ぐことができます。

タブパネル実装 - JS - 7
tab.click(function(){
    var targetTabId = $('a',this).attr('hash');
	
    tab.removeClass('active');
    tabPanel.hide();
    $(this).addClass('active');
    $(targetTabId).show();
	
    return false;
});

では、書いてきたスクリプトを合わせて確認してみましょう。

タブパネル実装 - JS - 8
jQuery(function($){
    var tabArea = $('div.tabArea');
    var tabPanel = $('div.tabPanel',tabArea);
    var tab = $('ul.tab li',tabArea);
	
    tabPanel.not(':first').hide();
	
    tab.eq(0).addClass('active');
    tab.click(function(){
        var targetTabId = $('a',this).attr('hash');
		
        tab.removeClass('active');
        tabPanel.hide();
        $(this).addClass('active');
        $(targetTabId).show();
		
        return false;
    });
});

開いておきたいタブを選択できるように改良する

上記で、タブパネルのベースが完成しました。しかし上記のスクリプトでは、一つ目のタブが強制的に選択された状態になります。機能を少し追加して、li要素のclassにactiveがあれば、そのタブを優先して開いておくように改良してみましょう。

下記の部分で一つ目のタブを開いた状態にしているので、まずはこれをやめます。

タブパネル実装 - JS - 9
tabPanel.not(':first').hide();
	
tab.eq(0).addClass('active');

残りの手順としては

  • eachメソッドを使い、classにactiveをもっているタブを探す
  • activeをもっていれば、click時と同じ動作をさせる
  • eachメソッド終了後に、activeになっているタブがなければ、最初のタブを表示状態にする

まずはeachメソッドを使ってclassにactiveをもっているタブを探して、あればclick時と同じ動作をさせる

2つ同時にやってしまいます。

タブパネル実装 - JS - 10
tab.click(function(){
    // a :
    var targetTabId = $('a',this).attr('hash');
    
    tab.removeClass('active');
    tabPanel.hide();
    $(this).addClass('active');
    $(targetTabId).show();
    // : a
    
    return false;
}).each(function(){
    if($(this).hasClass('active')){ // ※1
        // b :
        var targetTabId = $('a',this).attr('hash');
        
        tab.removeClass('active');
        tabPanel.hide();
        $(this).addClass('active');
        $(targetTabId).show();
        // : b
    }
});

eachメソッドで順番に回しているタブのclassにactiveがあるかどうかを確かめるには、※1の箇所のようにhasClassメソッドを使います。classにactiveがあればtrueが、なければfalseが返されます。⁠if(条件値){ 処理1 }else{ 処理2 }」は、括弧内がtrueなら処理1、falseなら処理2を行い、サンプルのようにelse以下を省略することもできます。また、click時とeachメソッド内で実行する内容全く同じなので(// a : : a//と// b : : b //の部分⁠⁠、以下のようにひとつにまとめてしまうことにより、より見やすくシンプルなソースコードになります。

タブパネル実装 - JS - 11
// functionとしてまとめる
var tabSet = function(target){
    var targetTabId = $('a',target).attr('hash');
    
    tab.removeClass('active');
    tabPanel.hide();
    $(target).addClass('active');
    $(targetTabId).show();
}

tab.click(function(){
    tabSet(this); // thisを引数として渡す
    return false;
}).each(function(){
    if($(this).hasClass('active')){
        tabSet(this); // thisを引数として渡す
    }
});

実行時にタブを渡す必要があるので(thisで指定いた部分⁠⁠、まとめたfunction tabSetではthisを引数で渡せるようにしておきます。functionに置き換えるときは、thisだった部分をtargetに置き換えるだけです。

eachメソッド終了後に、activeになっているタブがなければ、最初のタブを表示状態にする

最後に選択されているタブがない場合の処理を追加します。選択されているタブがないということは、開いているタブパネルがひとつではない、つまり全てのタブパネルが開いている状態なので、tabPanel.filter(':visible').length!=1と表すことができます。これをif文の条件値に入れることで、そのときだけに処理を実行することができます。

ここで、ベースのスクリプトにあった、初期化処理を行えばいいのです。

タブパネル実装 - JS - 12
if(tabPanel.filter(':visible').length!=1){
    tab.eq(0).addClass('active');
    tabPanel.not(':first').hide();
}

これまでのソースコードをまとめるたものが以下になります。

タブパネル実装 - JS - 13
jQuery(function($){
    var tabArea = $('div.tabArea');
    var tabPanel = $('div.tabPanel',tabArea);
    var tab = $('ul.tab li',tabArea);
    
    var tabSet = function(target){
        var targetTabId = $('a',target).attr('hash');
        
        tab.removeClass('active');
        tabPanel.hide();
        $(target).addClass('active');
        $(targetTabId).show();
    }
    
    tab.click(function(){
        tabSet(this);
        return false;
    }).each(function(){
        if($(this).hasClass('active')){
            tabSet(this);
        }
    });
    
    if(tabPanel.filter(':visible').length!=1){
        tab.eq(0).addClass('active');
        tabPanel.not(':first').hide();
    }
});

以上で「開いておきたいタブを選択できるように改良」したタブパネルスクリプトが完成しました。

今回のまとめ

タブパネルを実装する、ということをもしかすると最初は、難しい、と感じたかも知れません。しかし、完成したコードを見てもらえれば分かる通り、jQueryを使えばたった20数行のシンプルなコードで実装することができてしまいました。

jQueryを使えば、実行したい命令文を箇条書きのように並べたり、メソッドチェーンで繋いでいくだけでよいのです。

次回は、今回作成したこのシンプルなタブパネルスクリプト、jQueryプラグインにしてみたいと思います。

おすすめ記事

記事・ニュース一覧