IMG_5256
Development

Vanilla JavaScriptを書いてみよう その2(タブUIを実装)

前回はイベントハンドリングとjQueryの様にクラス名でhtml要素を検索出来るquerySelectorを紹介しました。

今回はjQuery等の等のライブラリを一切使わずにタブUIを実装して見るための下準備をご紹介したいと思います。

デザイン

デザインは以下の様なもので、タブのボタンとその内容の文章が3つずつ存在し、選択されていないタブをクリックすると内容と共に選択中の表示に切り替わります。

スクリーンショット 2014-02-24 12.23.24

 

各タブに .tab-nav__item–current というクラスが付いていると選択中の表示になり、内容部分はデフォルトで display: none; に設定されているので .tab-content–current というクラスが付いているもののみ display: block; で可視化するという仕様です。

見た目の制御はcssで行うようにしていますので、JavaScriptからhtml要素のclass属性を操作できる必要があります。

クラスを定義してみる

class属性は element.className というプロパティを使って操作できますので、専用のクラス(ここで言うクラスはインスタンスを生成するためのオブジェクトという意味)を用意しておきましょう。

ところがJavaScriptにはクラスを定義する構文は用意されていません。ではどうやるのかというと、関数オブジェクトに備わっているprototypeという仕組みを利用することでクラス定義をエミュレート出来ます。
具体的には以下のように記述します。

var Person = (function() {
  function Person(name) {
    this.name = name;
  }

  Person.prototype.greet = function() {
    alert('Hello ! My name is ' + this.name);
  }

  return Person;
)();

Personクラスは初期化時に name という変数を受け取り自身の name プロパティに格納します。そして greet というメソッドを持っていて、nameプロパティを連結した挨拶文をアラートする動作をします。

このPersonクラスからインスタンスをインスタンスを生成するには new Person(‘name’) という形で初期化関数を実行します。

var yamagata = new Person('yamagata');

greetメソッドの実行はこうです

yamagata.greet();

Elementクラス

そして今回のために実装したElementクラスはこちらです。

var Element = (function() {
  function Element(el) {
    this.el = el;
    this.id = this.el.id;
  }

  Element.prototype.classes = function() {
    return this.el.className.split(/\s+/);
  };

  Element.prototype.removeClass = function(targetClass) {
    var classes = this.classes();
    for (var i in classes) {
      if (classes[i] === targetClass) {
        classes.splice(i, 1);
      }
    }
    this.el.className = classes.join(' ');
  };

  Element.prototype.addClass = function(targetClass) {
    var classes = this.classes();
    for (var i in classes) {
      if (classes[i] === targetClass) {
        return
      }
    }
    classes.push(targetClass);
    this.el.className = classes.join(' ');
  };

  Element.prototype.hasClass = function(targetClass) {
    var classes = this.classes();
    var exist = false;
    for (var i in classes) {
      if (classes[i] === targetClass) {
        exist = true;
      }
      if (exist) break;
    }
    return exist;
  };

  Element.prototype.isEqual = function(targetElement) {
    return this.el === targetElement;
  };

  return Element;
})();

class属性を操作する場合、複数のclassを扱うことを考慮しなければいけないので、スペースで分割した配列を返す classes メソッドを実装しました。
addClass と removeClass はその配列に対して操作を行う仕組みです。

配列に要素を追加する場合は、末尾に追加する push というメソッドがメソッドが便利です。
また、配列から要素を削除する場合は、spliceというメソッドが便利です。これは削除したい要素のインデックスとそのインデックスの位置から何個の要素を削除するかという個数を指定します。今回は常に1つの要素を消すように 2番目の引数には 1 を渡しています。

var fruits = ['apple', 'banana', 'melon'];
fruits.splice(1, 1); // => bananaが削除される

また特定のclass属性を持っているかチェックをする hasClass と html要素自体が一致しているかをチェックする isEqual も実装しておきました。

このElementクラスを使用するためにはhtml要素を渡して new しなければいけませんので、以下のようにしてタブのボタンをElement化します。

var tabNavItems = [];
var tabNavItemElements = document.querySelectorAll('.tab-nav__item');
for (var i in tabNavItemElements) {
  if (typeof tabNavItemELements[i] === 'object') {
    tabNavItems.push(new Element(tabNavItemElements[i]));
  }
}

.tab-nav__item が付いているhtml要素を全て取得し、1つずつnew Elementしているのですが、typeofで取り出した要素が確実にhtml要素であることを確認しています。
これはfor文では配列のプロパティである length (中身は整数)も取り出してしまうためです。

まとめ

class属性を操作する準備は整いました、普段jQueryで行っている処理もわりと簡単に作れてロジックを書く勉強にもなるのでおすすめです。一度くらい車輪の再発明をしてみるのもいいと思います。
次回はクリックイベントを監視してどのようにタブの切替を行うのかをご紹介します。

標準