これでできる! クロスブラウザJavaScript入門

第21回 JavaScriptによるUIの実装:タブメニュー編

この記事を読むのに必要な時間:およそ 6 分

こんにちは。前回から引き続き,JavaScriptによるUIを実装する方法を紹介していきます。

基本のタブメニュー

ウェブアプリでよく使われるインタフェースのひとつ,タブメニューを実装してみましょう。まず,骨組みとなる基本のHTMLは以下のとおりです。

タブメニューの基本HTML

<div class="js-tabs">
  <ul id="tab_menu1" class="tab_menu">
    <li><a href="#page1-1">Page 1</a></li>
    <li><a href="#page1-2">Page 2</a></li>
    <li><a href="#page1-3">Page 3</a></li>
  </ul>
  <div id="tab_content1" class="tab_content">
    <div id="page1-1" class="page">
      Page 1
    </div>
    <div id="page1-2" class="page">
      Page 2
    </div>
    <div id="page1-3" class="page">
      Page 3
    </div>
  </div>
</div>

そして,CSSは次のとおりです。ひとまず今回はサイズを固定にして,特に凝ったスタイルは使っていません。あえて言えば,li要素をinlineにして横に並べている点と,リンクに対してアクティブなとき・マウスを乗せたときで,スタイルを変えている点がポイントです。

タブメニューの基本CSS

.js-tabs ul.tab_menu{
  list-style-type:none;
  margin:0px;
  padding:0px;
}
.js-tabs ul.tab_menu li{
  display:inline;
  background:#666;
  margin:0px;
  padding:2px;
}
.js-tabs .tab_menu li a{
  padding:3px;
}
.js-tabs .tab_menu li a:link,
.js-tabs .tab_menu li a:visited{
  background-color:#666;
  color:#fff;
}
.js-tabs .tab_menu li a.active:link,
.js-tabs .tab_menu li a.active:visited{
  background-color:#444;
  color:#fff;
}
.js-tabs .tab_menu li a:hover{
  background-color:#333;
  color:#f0f;
}
.tab_content{
  position:relative;
  height:200px;
}
.tab_content div.page{
  width:450px;
  height:200px;
  position:absolute;
  color:#222;
}
#page1-1{
  background-color:#ffa;
}
#page1-2{
  background-color:#faf;
}
#page1-3{
  background-color:#aff;
}

さて,このHTMLを操作するJavaScriptです。まずはなるべくシンプルに実装してみます。

タブメニューの基本JavaScript

(function(){
var menu = document.getElementById('tab_menu1');
var content = document.getElementById('tab_content1');
var menus = menu.getElementsByTagName('a');
var current; // 現在の状態を保持する変数
for (var i = 0, l = menus.length;i < l; i++){
  tab_init(menus[i], i);
}
function tab_init(link, index){
  var id = link.hash.slice(1);
  var page = document.getElementById(id);
  if (!current){ // 状態の初期化
    current = {page:page, menu:link};
    page.style.display = 'block';
    link.className = 'active';
  } else {
    page.style.display = 'none';
  }
  link.onclick = function(){
    current.page.style.display = 'none';
    current.menu.className = '';
    page.style.display = 'block';
    link.className = 'active';
    current.page = page;
    current.menu = link;
    return false;
  };
}
})();

タブを切り替えるには,クリックしたリンクに対応するコンテンツを表示するだけでなく,それまで表示されていたコンテンツを非表示にする必要があります(ついでにリンクのスタイルも変更)⁠今回は現在表示されているコンテンツ・リンク自体を変数(current)に入れておき,現在のコンテンツを非表示に変更してから,次のコンテンツを表示する,という方法で実装しました。

なお,for文でのループの中でtab_init関数を呼び出していますが,この関数の中身をfor文の中に直接書いても問題ないように見えると思います。しかし,実際には直接書いてしまっては意図通りに動作しなくなります。その理由はもちろんクロージャーです。

上記コードにおいて,tab_init内のlink・pageなどの変数はtab_init内のローカル変数であり,同時にtab_init内部のonclickに設定された関数からもlinkとpageは参照可能です。

つまり,変数のスコープは次のようになっています(こちらは実際に動くコードではありません)⁠

クロージャーと関数

function (){
  var current;
  function (){ // #1
    var page, link;
    function(){
      // onclick
    }
  }
  function (){ // #2
    var page, link;
    function(){
      // onclick
    }
  }
  function (){ // #3
    var page, link;
    function(){
      // onclick
    }
  }
}

変数pageとlinkがそれぞれ3つ存在する点がポイントです。

クロージャーの復習はこれくらいにして,実際の動作サンプルは次のとおりです。

Page 1
Page 2
Page 3

機能的には問題ありませんが,リンクの間の隙間が気になると思います。これは,⁠li要素をインラインにしているので)li要素の間にある改行が空白文字列として認識されてしまうためです。

これを回避する方法はいくつかあり,単純にliとliの間の改行を取り除くというのが手軽な方法です。

または,HTMLではli要素は終了タグを省略できること,タグを省略した場合に改行がその前のタグの中身になる点を利用する方法もあります(加えて,li要素をinlineではなくinline-blockにする必要があります)⁠ただ,今回のケースではIEでリンクの後ろに空白が残ってしまいます。

というわけで,今回は改行の位置を工夫してリンクの中に空白を入れるようにしてみます。

タブメニューの基本HTML(改行位置の変更)

<div class="js-tabs">
  <ul id="tab_menu1" class="tab_menu">
    <li><a href="#page1-1"> Page 1 
    </a></li><li><a href="#page1-2"> Page 2
    </a></li><li><a href="#page1-3"> Page 3
    </a></li>
  </ul>
  <div id="tab_content1" class="tab_content">
    <div id="page1-1" class="page">
      Page 1
    </div>
    <div id="page1-2" class="page">
      Page 2
    </div>
    <div id="page1-3" class="page">
      Page 3
    </div>
  </div>
</div>
Page 1
Page 2
Page 3

著者プロフィール

太田昌吾(おおたしょうご,ハンドルネーム:os0x)

1983年生まれ。JavaScriptをメインに,HTML/CSSにFlashなどのクライアントサイドを得意とするウェブエンジニア。2009年12月より、Google Chrome ExtensionsのAPI Expertとして活動を開始。

URLhttp://d.hatena.ne.jp/os0x/

コメント

コメントの記入