jQuery:使い勝手の良いタブ機能の実装メモ


#はじめに
`jQuery UI` や `Bootstrap` のようなライブラリ/フレームワークを使用せずに、簡単なタブメニューを作成してみました。なるべく使い勝手が良く（カスタマイズしやすく）なるような仕様を想定しました。何かのお役に立てば幸いです。



#仕様

- スクリプトはなるべくhtmlに依存させない
- 表示・非表示・スタイルに関する操作は全てCSS側で行う
- 直接タブへリンクできるようにする (*1
- なるべく多くのブラウザで動作させる (*2
- JavaScript無効時も想定する

> *1)
> 例えば別ページより`http://xxxx/#view`とリンクされた場合は、該当するタブとコンテンツをデフォルト表示

> *2)
> IE6以上で基本動作を確認しました（IETesterですが・・・）





#前提条件
- jQuery1系を使用
- タブの**hrefと切り替えたい要素のidを一致**させる（index.html#hashのようにはせずに#hashとする） (*3

> *3)
> このようなタブメニューであえてindex.html#hashのようにするケースは、あまり無いかと思うので考慮していません。


#ソースコード

```tab.html
<ul class="tabs">
  <li><a href="#view1">タブ1</a></li>
  <li><a href="#view2">タブ2</a></li>
  <li><a href="#view3">タブ3</a></li>
  <li><a href="#view4">タブ4</a></li>
</ul>

<div id="view1" class="tab-content">タブ1の中身です</div>
<div id="view2" class="tab-content">タブ2の中身です</div>
<div id="view3" class="tab-content">タブ3の中身です</div>
<div id="view4" class="tab-content">タブ4の中身です</div>
```

```tab.css
.tabs .is_active {
  /* 何かタブを変更する設定 */
}
.tab-content {
  display: none;
}
.tab-content.is_show {
  display: block;
}
```

```tab.js
$(function() {

  var tabMenu = function() {

    /**
     * 変数の指定
     * $tabs              : tabの親要素のjQueryオブジェクト
     * $content           : tabによって切り替わる要素のjQueryオブジェクト
     * TAB_ACTIVE_CLASS   : tabが選択されたスタイルを変更するclass名
     * CONTENT_SHOW_CLASS : contentを表示させるためのclass名
     * id_arr             : $contentのIDを配列に格納
     */
    var $tabs              = $('.tabs');
    var $content           = $('.tab-content');
    var TAB_ACTIVE_CLASS   = 'is_active';
    var CONTENT_SHOW_CLASS = 'is_show';
    var id_arr             = $content.map(function() { return '#' + $(this).attr('id');}).get();


    /**
     * 該当するhashデータがある場合、hashを返す
     * 該当とは id_arr[] に含まれるもの
     * @return {string} 該当する場合
     * @return {false} 該当しない（存在しない）場合
     */
    var getHash = function() {
      var hash = window.location.hash;
      var index = id_arr.indexOf(hash);

      if (index === -1) {
        return false;
      } else {
        return id_arr[index];
      }
    };


    /**
     * ページ読み込み時に実行
     * 1. hashがあれば、hashをhrefに持つタブのスタイル変更（専用のclass付与）
     * 2. hashがあれば、hashをidに持つコンテンツを表示（専用のclassを付与）
     * 3. hashがなければ、タブの先頭が選択された状態とする
     */
    var initialize = function() {
      var hash = getHash();

      if (hash) {
        $tabs.find('a[href="'+hash+'"]').addClass(TAB_ACTIVE_CLASS); // 1
        $(hash).addClass(CONTENT_SHOW_CLASS); // 2
      } else {
        $tabs.find('li:first > a').addClass(TAB_ACTIVE_CLASS); // 3
        $($content[0]).addClass(CONTENT_SHOW_CLASS); // 3
      }
    };


    /**
     * タブのクリックイベント
     * 1. クリックされたタブのhref, 該当するcontentを取得
     * 2. 既にクリック済みの状態であればスキップ
     * 3. 一旦タブ・contentの専用classを全削除
     * 4. クリックしたタブのスタイルを変更、該当するcontentを表示（それぞれ専用のclassを付与）
     */
    var addEvent = function() {
      $tabs.find('a').on('click', function() {
        var href = $(this).attr('href'); // 1
        var $targetContent = $(href); // 1

        // 2
        if ($(this).hasClass(TAB_ACTIVE_CLASS)) {
          return false;
        }

        // 3
        $tabs.find('a').removeClass(TAB_ACTIVE_CLASS);
        $content.removeClass(CONTENT_SHOW_CLASS);

        // 4
        $(this).addClass(TAB_ACTIVE_CLASS);
        $targetContent.addClass(CONTENT_SHOW_CLASS);

        return false;
      });
    };

    return [initialize(), addEvent()];
  };

  // 実行
  tabMenu();

});
```

> 実際の動作・ソースコードは以下で確認できます
> - http://codepen.io/nekoneko-wanwan/pen/YywEZR
> - http://codepen.io/nekoneko-wanwan/pen/YywEZR#view2
> - http://codepen.io/nekoneko-wanwan/pen/YywEZR#view3
> - http://codepen.io/nekoneko-wanwan/pen/YywEZR#view4





##一口メモ

- class名を変えたい場合は、スクリプトの上の方にある大文字で書いている変数を変更します
- `.tab-content`はcssで非表示をデフォルトにしておきます
- `.tab-content`に`.is_show`で表示させるスタイルを適用させます
- tab（aタグ）は`.is_active`で選択時のスタイルを適用させます





#JavaScript無効時に対応する

上記だけではJavaScript無効時には対応できていません。対応には以下のようなパターンが考えられます。
※ここでの対応とは「コンテンツが全て表示される」ということを指します

- **`.js`**が取れる場合
- 専用のCSSを用意する
- スクリプトを改修する



##.jsが取れる場合
**Modernizer**などで**`.js`**（jsが有効状態を表すclass名が自動で付く）を取得できる場合は、`.js .tab-content {display: none;}`とすればOKかと思います。


##専用のCSSを用意する
**`<noscript>`**で専用のCSSファイルもしくは、**`<style>`タグ**を指定します。その中で`.tab-content {display: block;}`を設定してやればOKかと思います。

> html5からheadタグ内で`<noscript>`が使えるようになりました


##スクリプトを改修する
スクリプト内でデフォルトを非表示にさせるようします。例えば`.is_hide`を用意し、動作に合わせて要素にクラスを付与するなど。ですが多くの場合、実行処理の順番により読み込み時に**隠れるべき要素が一瞬表示**されてしまいます。





#番外編
少しリッチなUIになるよう調整してみました。もちろん調整はCSSのみです。（css3を使っているのでモダンブラウザのみです）


**【動作イメージ】**

![tab.gif](https://qiita-image-store.s3.amazonaws.com/0/58436/ed9d8fd7-596b-300b-ff4e-83c8b7b7876c.gif)



> 実際の動作・ソースコードは以下で確認できます
> http://codepen.io/nekoneko-wanwan/pen/VverGR







#終わりに

```
 ε  ⌒ﾍ⌒ヽﾌ
（   ( ・ω・）  これはブタ
  しー し─Ｊ
```