先日Yokohama.jsでちょろっとお話しして来ました。その内容を再構築して掲載します。

先に完成品

こんな感じのものをつくります。

とてもとても簡単な、jQueryプラグインのつくりかた。 – jsdo.it – share JavaScript, HTML5 and CSS

仕様

クリックするたびに色が変わる、というものにしましょう。色は引数で渡します。

こんな感じで使います。

$('.rotate_colors').rotateColors(['red', 'green', 'blue']);

では作ってみましょう。

インターフェイスを作る

まずは外側から作ってゆきましょう。

(function($) {
  $.fn.rotateColors = function(colors) {
    return this.each(function(i, elem) {
      // TODO : write codes.
    });
  };
}(jQuery));

こんなに短いコードですが、jQueryプラグインを作る上で忘れられない、5つのポイントがあります。

  1. $がjQueryである事を期待しない。
  2. jQuery.fnにメソッドを定義する。
  3. 実行コンテキストthisはjQueryオブジェクト。
  4. .each()を使って処理する。
  5. thisをreturnする。

他のプラグインと併用し、$がjQueryを指していない場合も考慮して、直接$は使いません。全体を無名関数にして、その引数としてjQueryを$という名前で受け取るようにしましょう。

jQuery.fnのプロパティとして関数を設置しておくと、それが$(elem)のメソッドとして呼び出せるようになります。またその際の「実行コンテキスト」つまりthisは、当然ですがメソッドを呼び出しているjQueryオブジェクトになります。このときに複数の要素を内包している場合がある($('.hoge')を想像してください)ので、.each()を使って各要素に対して個別に処理するように実装する必要があります。

最後にメソッドチェインのためにjQueryオブジェクトを返します。ここではthis.each()の戻り値をそのままreturnすれば良いでしょう。

これで外枠は完成です。中身を書いていきましょう。

設定を保存する

方法は色々ありますが、要素ごとに .data()で記憶してしまう方が安全で簡単でしょう。

(function($) {
  $.fn.rotateColors = function(colors) {
    return this.each(function(i, elem) {
      $(elem)
        .data('rotateColors.colors', colors)
        .data('rotateColors.index', 0);
    });
  };
}(jQuery));

indexは順番に表示するとき、今どこを表示しているかの情報です。

最初の処理

プラグイン使用直後には最初の色になるようにします。その方が自然でしょう。

(function($) {
  $.fn.rotateColors = function(colors) {
    return this.each(function(i, elem) {
      $(elem)
        .data('rotateColors.colors', colors)
        .data('rotateColors.index', 0)
        .css('color', colors[0]);
    });
  };
}(jQuery));

クリック時の処理をする

一歩ずつ進めましょう。まずは外枠、.click()でクリックを拾います。

(function($) {
  $.fn.rotateColors = function(colors) {
    return this.each(function(i, elem) {
      $(elem)
        .data('rotateColors.colors', colors)
        .data('rotateColors.index', 0)
        .css('color', colors[0])
        .click(function(event) {
            // TODO : write code
        });
    });
  };
}(jQuery));

さて、順番に色を変える処理の内容は想像付くでしょうか? こんな感じになります。

  1. 次の色を得る。
  2. 次の色を設定する。
  3. 現在の色の位置を保存する。

次の色を得る

まずは保存しておいた情報を取り出します。

(function($) {
  $.fn.rotateColors = function(colors) {
    return this.each(function(i, elem) {
      $(elem)
        .data('rotateColors.colors', colors)
        .data('rotateColors.index', 0)
        .css('color', colors[0])
        .click(function(event) {
            var $el = $(this);
            var colors = $el.data('rotateColors.colors'); 
            var index = $el.data('rotateColors.index');
        });
    });
  };
}(jQuery));

次の色を設定する

現在の色はcolors[index]のはずですので、その次はcolors[index+1]になります。ただし、このときcolorsの中身を超えてしまわないよう、超えたら0に戻るようにします。そういう計算は%が便利です。

(function($) {
  $.fn.rotateColors = function(colors) {
    return this.each(function(i, elem) {
      $(elem)
        .data('rotateColors.colors', colors)
        .data('rotateColors.index', 0)
        .css('color', colors[0])
        .click(function(event) {
            var $el = $(this);
            var colors = $el.data('rotateColors.colors'); 
            var index = $el.data('rotateColors.index');
            index = (index + 1) % colors.length;
            $el.css('color', colors[index])
        });
    });
  };
}(jQuery));

現在の色の位置を保存する

おっと、進めたindexを保存しなおすのを忘れないでくださいね。

(function($) {
  $.fn.rotateColors = function(colors) {
    return this.each(function(i, elem) {
      $(elem)
        .data('rotateColors.colors', colors)
        .data('rotateColors.index', 0)
        .css('color', colors[0])
        .click(function(event) {
            var $el = $(this);
            var colors = $el.data('rotateColors.colors'); 
            var index = $el.data('rotateColors.index');
            index = (index + 1) % colors.length;
            $el
                .css('color', colors[index])
                .data('rotateColors.index', index);
        });
    });
  };
}(jQuery));

これでクリックするたびに色が変わるようになりました。

完成です!

発展

動くようになりましたが、もっと機能を追加したい場合はどうしたら良いでしょうか。例えば色リストを変更するとか、クリックしてももう変わらないようにするとか。

jQueryプラグインでよく使われるのは、引数に文字列でコマンドを渡してやるやり方です。

そうするとだんだん規模が大きくなってきますので、関数を分割したりといった工夫も必要になってくるでしょう。

まあ色々試してみてください。

参考