jQueryを使わない汎用型タブメニューUI

https://hacknote.jp/archives/3874/

上記のソースの場合、各タブにIDが固定で振られているため、タブUIを複数の箇所で使いたくなった場合それらのID全てを変更しなければならず、気軽にタブも増やしにくいなど汎用性に欠けています。

JSの実行速度で言えば単純にID直指定が早いので上記の方法も価値はありますが、今回は汎用的なUIとしてどこでも何度でも使えるパーツを作ります。

HTML

タブUI用のHTMLは下記を前提とします。

大きくは「tabMenu」でくくり、その下にクリックするタブの「tab」と、表示コンテンツ用の「tabContents」が存在します。

「tab」内のリンクでは関数「tabUI」を呼び出しています。
引数としてthisと「tabMenu」自身にかかっているIDの「tabMenu」を投げていますが、thisはクリックされたタブを把握するため、IDは対象となるタブメニューを指定するための引数になります。

<div class="tabMenu" id="tabMenu">
    <ul class="tab">
        <li class="active">
            <a href="javascript:void(0)" onclick="tabUI(this,'tabMenu')">タブA</a>
        </li>
        <li class="">
            <a href="javascript:void(0)" onclick="tabUI(this,'tabMenu')">タブB</a>
        </li>
        <li class="">
            <a href="javascript:void(0)" onclick="tabUI(this,'tabMenu')">タブC</a>
        </li>
    </ul>
    <ul class="tabContents">
        <li class="active">aaa</li>
        <li class="">bbb</li>
        <li class="">ccc</li>
    </ul>
</div>

HTMLタグを組む時点で処理を考えておく必要がありますが、今回は「どのタブが押されたか」という事を記録し、タブとコンテンツのリストを総なめして押されたタブ番号と等しいタブ・コンテンツを有効化するという考えで組みます。

JavaScript

function tabUI($this, $target) {
    var target = document.getElementById($target);  //タブUIの大本取得
    var childs = target.getElementsByTagName("ul"); //タブUI直下2つのUL取得
    //childsからタブ側のliとコンテンツ側のliを取得
    for(i=0; i<childs.length; i++) {
        if(childs[i].className === "tab") {
            var tab = childs[i].getElementsByTagName("li");
        } else if(childs[i].className === "tabContents") {
            var tabContents = childs[i].getElementsByTagName("li");
        }
    }
    //クリックしたタブがアクティブか調べる
    var flag = -1;  //クリックしたタブ番号を兼ねたフラグ
    for(i=0; i<tab.length; i++) {
        if(tab[i] === $this.parentNode) {
            if(tab[i].className.indexOf("active") < 0) {
                //クリックしたタブがアクティブでなければ何個目のタブかを取得
                var flag = i;
            }
        }
    }
    //アクティブでないタブがクリックされていれば実行
    if(flag >= 0) {
        //タブ番号と等しいタブはactive付与、そうでなければactive除去
        for (i = 0; i < tab.length; i++) {
            if (i === flag) {
                tab[i].className += " active";
            } else {
                tab[i].className = tab[i].className.replace("active", "");
            }
        }
        //タブ番号と等しいコンテンツはactive付与、そうでなければactive除去
        for (i = 0; i < tabContents.length; i++) {
            if (i === flag) {
                tabContents[i].className += " active";
            } else {
                tabContents[i].className = tabContents[i].className.replace("active", "");
            }
        }
    }
}

styleの操作ではなくクラス「active」の付与・削除でタブUIを表現しています。

上記の処理でIDに依存しているのは大本のタブUIのIDのみであり、HTML側で一部書き換えるのみで対応できます。タブの個数も配列の数からループをかけているので何個でも対応できます。

なおポイントとして、クラス「active」を付与する際に” active”と冒頭にスペースを一つ開けています。スペースを開けていないと他のクラスが存在していた場合に例えば「centeractive」など直結して無効化してしまうため、「center active」とするための処置です。このように「意図から外れるがありえる状況」も予測して対処しておくことで、汎用性がさらに高まります。

UIを作る際は一つの絵のように固定化して考えず、機能として動的に要素が置き換わることを前提に考えるのが重要です。