DOMおれおれAdvent Calendar 2015 – 23日目

2015-12-23

jQueryをまるまる使う程じゃないけどちょっとjQuery的な機能が欲しいなという場面はときどきあるかと思うんですが、そういうときに「jQuery風の機能」を自作するための実装例を幾つか挙げてみたいと思います。

セレクターから検索して、各要素を操作

こんな風に使いたい場合。

var $el = $('#foo .bar [boo=123]');
$el.css({ color:'red' });

以下の二つの機能が必要です。

  • セレクターから要素を取得し、確保しておく
  • 確保した要素全てに対して処理をする

他にも諸々の機能が必要かと思いますが、それらの基盤となるのが本機能ですね。

セレクターから検索して要素を確保

これで配列風に扱えるようになります。

// インターフェイス。これを使う。
function $(selector) {
  return new MyQuery(selector);
}

// コンストラクター。実態。
function MyQuery(selector) {
  var nodes = document.querySelectorAll(selector);

  // 配列風に各要素を保持
  for (var i=0, l=nodes.length; i<l; i++) {
    this[i] = nodes[i];
  }

  // 配列風に要素数を保持
  this.length = nodes.length;
}
var $links = $('a');
console.log($links.length, $links[0]);

といっても何も機能がないので大して嬉しくないですね。ここからです。

各要素に対して一括操作

forEach() なものを実装します。というか配列が持つ関数をそのまま使っちゃって大丈夫です。

MyQuery.prototype.forEach = Array.prototype.forEach;

あら簡単。(IE 9+。IE 8の場合は自前で実装します。) これだけでもだいぶ使えると思います。

var $links = $('a');
$links.forEach(function(el, index) {
  console.log(el, index);
});

ちなみにjQueryは forEach() じゃなくて each() というAPIで、コールバック関数に与えられる引数の順序が違います。けどまあいいよね。

API設計の基盤思想

jQueryは使い心地が良いのですが、以下の共通仕様が大きく貢献しているものと思います。

  • メソッドチェインのため、操作後オブジェクト自身を返す
  • 対象の有無による分岐を省くため、検索結果が0件の状態でも各機能でエラーにしない

というわけで、これらに気を付けながら諸々実装していきます。

イベント監視

割とよく使う気がしますので、これからいきましょう。 on()addEventListener() で実装できます。

MyQuery.prototype.on = function(type, listener) {
  this.forEach(function(el) {
    el.addEventListener(type, listener);
  });
  return this;
};

簡単だ!

拡張しやすくする

jQueryのメソッドは $.fn に登録する事で自由に追加できるようになっています。真似ておきましょう。

$.fn = MyQuery.prototype;

できたよ

全然APIないですが、基本的にはこんなもんでしょうか。

// インターフェイス。これを使う。
function $(selector) {
  return new MyQuery(selector);
}

// コンストラクター。実態。
function MyQuery(selector) {
  var nodes = document.querySelectorAll(selector);

  // 配列風に各要素を保持
  for (var i=0, l=nodes.length; i<l; i++) {
    this[i] = nodes[i];
  }

  // 配列風に要素数を保持
  this.length = nodes.length;
}

$.fn = MyQuery.prototype;
$.fn.forEach = Array.prototype.forEach;

$.fn.on = function(type, listener) {
  this.forEach(function(el) {
    el.addEventListener(type, listener);
  });
  return this;
};

30行にも満たないコードですが、これでこんな風に使えます。

$('#el').on('click', function(event) {
  alert('!');
});

ちょっとサンプルコード書きたいけどjquery.jsを探してくるの面倒だなーみたいなときに便利かもしれません。良い感じに雛型が整備できるなら良いけれど。

というのライブラリーを作ってます

よければご利用ください。