前の記事に引き続き、ビジュアル情報処理研究合宿のHPで使用したネタを書いていきたいと思います(たぶん、コレで最後)。

jQueryによる共通部品の読み込み

ヘッダーやフッター、メニューバーなどの共通部品を1つのページごとに実装することは保守管理の面であまりよい状態ではありません。

先輩によると昨年度のページではSSIを用いて共通部品を読み込んでいたそうですが、今年度はBootstrapに関連して全編JavaScriptもしくはjQueryでこれらを実現しようと思いました。とは言っても、やり方はめちゃくちゃ簡単です。

まず、共通部品を格納するブロック要素にidを割り当てます。

    <!-- Header -->
    <div class="row" id="Header">
    </div>

    <!-- Footer -->
    <div class="row" id="Footer">
    </div>

次に、この部分に挿入したいHTMLを別ファイルで作成しておきます。このファイルをjQuery側で開き、HTML上の指定idに対して読み込みます。

    $(function() {
      $("#Header").load("./files/to/header.html");
      $("#Footer").load("./files/to/footer.html");
    });

この1行だけで、当該id部分に別ファイルのHTMLが読み込まれるため、ヘッダーやフッターを1ファイルにしておけば、あとは適当な場所で読み込むだけで全て共通の内容でレンダリングされることとなります。簡単ですね!

ただし、ローカルファイルからの読み込みはChromeではクロスドメイン制限などによって実行されないため、起動オプション指定によってこれを解除するか、デバッグには他のブラウザを使用する必要があることに注意が必要です。

一方で、メニューは共通だけど先の記事におけるNavbarのように「アクティブにしたい項目がページごとに異なる」場合、共通部品の読み込みだけでは対応できません。何らかの形でアクティブにする項目を指定し、HTMLを変更する必要があります。

このため、今回のページ作成ではメニューバーの項目を動的に追加し、HTML側のdata属性で指定された項目をアクティブにしてリンク先を#に変更する処理を行うことで、部品自体を共通化しながらフレキシブルにアクティブ項目の変更を実現しています。

まず、HTMLのNavbar側にdata属性を追加しておきます。ここではdata-activeを指定し、activeで指定されたメニュー項目をアクティブにすることとします。

    <!-- Navbar -->
    <div class="row" id="MyNavbar" data-active="0">
    </div>

次に、Navbarのテンプレートファイルを先ほどと同じように読み込みます。この時、読み込み自体は先程と同じですが、読み込み完了後にNavbarの各項目を追加する関数(generateNavbar)を実行するようにハンドラを指定します。

    $(function() {
      $("#MyNavbar").load("./files/to/menu.html", generateNavbar);
    });

また、この時のmenu.htmlを以下に示します。

    <nav class="navbar navbar-default navbar-static-top">
      <!-- Difinition for Mobile device view -->
      <div class="navbar-header">
       <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#my-navbar">
        <span class="sr-only">Toggle Navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
       </button>
       <a href="#" class="navbar-brand">メニュー</a>
      </div>

      <div class="collapse navbar-collapse" id="my-navbar">
       <ul class="nav navbar-nav nav-justified" id="NavbarContent">
       </ul>
      </div>
    </nav>

ここで、NavbarContentに対して各liコンテンツを追加していきます。追加を行うのが先に指定したgenerateNavbar関数になります。

    /* Definition of Navbar content */
    function NavbarContent(type, anchor, href) {
      this.Type = type;
      this.Anchor = anchor;
      this.Href = href;
    }

    /* Dynamic generator for Navigation Bar Content */
    function generateNavbar(eventObject) {
      var content = new Array();
      var ulElem = document.getElementById('NavbarContent');
      var activeNum = $('#Navbar').data("active");

      content[0] = new NavbarContent(null, 'Content 1', 'index.html');
      content[1] = new NavbarContent(null, 'Content 2', 'index2.html');
      content[2] = new NavbarContent(null, 'Content 3', 'index3.html');

      var i = 0;
      for(i in content) {
        if(i == activeNum) {
          content[i] = new NavbarContent("active", content[i].Anchor, '#');
        }

        var liElem = document.createElement('li');
        var aElem = document.createElement('a');

        if(content[i].Type != null) {
          liElem.className = content[i].Type;
        }
        aElem.textContent = content[i].Anchor;
        aElem.href = content[i].Href;

        liElem.appendChild(aElem);
        ulElem.appendChild(liElem);
      }
    }

ここでは、各項目のクラス要素名(Type)、リンク名(Anchor)、リンク先(Href)をデータ構造として定義し、配列化します。配列化したデータをリンクにして追加していきますが、この時にHTML側で指定されたdata-activeの値と配列要素のインデックスが一致する場所のクラス要素をactive指定し、リンク先も#に変更しています。

これによって、配列要素のインデックスは既知である必要はありますがHTML側でdata要素を指定するだけでNavbarを共通化しながらも変更したい部分をすっきりと指定し変更することが可能となります。実際の挙動はこちらからご確認ください。