※スマホ対応はしてません。

タグ: .on()

DOMでjQuery風ライブラリーを自作するための基本戦略です。(DOMおれおれAdvent Calendar 2015 – 23日目)

カテゴリー: JavaScript

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を探してくるの面倒だなーみたいなときに便利かもしれません。良い感じに雛型が整備できるなら良いけれど。

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

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

jQueryのon()をDOMでやるとaddEventListener()。ちょいと面倒だな。(DOMおれおれAdvent Calendar 2015 – 18日目)

カテゴリー: JavaScript

DOMおれおれAdvent Calendar 2015 – 18日目分

2015-12-18

jQueryだと on() で楽ちんなんだけど、DOMでは addEventListener() になります。な、長い!

var el = document.querySelector('#the-button');
el.addEventListener('click', function(event) {
  alert('Hello world!');
});

第三引数

ふつうは addEventListener(type, listener) という形で使うけれど、実は addEventListener(type, listener, useCapture) のように三つめの引数 useCapture を取る事ができます。

昔は必須だったけど今は省略可能で、省略時は false となります。

で、MDNを見るとこんな註釈が挿入されてます。(英語版にも同様の註釈あり。)

注記: useCapture は、主要なブラウザの最近のバージョンでのみ省略可能です。例えば、Firefox 6 より前では省略できません。幅広い互換性を保つために、引数を省略すべきではありません。

Firefox 6のリリースは2011年。IE と違って何年も前のものを使い続ける事はないだろうし、省略しちゃっても全然構わないと思います。

用途の説明は省きます。使わんし。

bind() の併用

addEventListener() で登録した関数は、実行時は対象の要素オブジェクトを this として呼ばれます。MVxのViewとかに分けて作ってるとそういうの困るので、 bind() して使う事が多いかと思います。

var view = {
  el: null,

  initialize: function(options) {
    this.el = options.el;
    this.name = options.name;

    this.el.addEventListener('click', this.onclick.bind(this));
  },

  onclick: function(event) {
    alert(this.name);  // => "Orange"
  }
};

view.initialize({
  el: document.querySelector('button'),
  name: 'Orange'
});

環境

IE 9+。他は問題なし。

IE 8までは attachEvent() という別のメソッドで同様の結果を得られます。ほとんど一緒だけど、イベント名の前に "on" を追加する必要があります。(例: "click" → "onclick" ) あとはコールバック関数に与えられる引数の内容がちょっと違うとか。

参考