素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: twitter: @javascripter にどうぞ。

Selectors APIのmatchesSelectorと、動的なページでのイベント処理

Selectors APIのDraftに、matchesSelectorというAPIが定義されていて、現在、WebKitとMinefieldのNightlyに搭載されている。
ただし、ドラフトなのでベンダープレフィックスがついていて、Element#webkitMatchesSelectorやElement#mozMatchesSelectorを使わなければいけない(というより、まだ直接使うには時期尚早だ)。
使い方は簡単。

var paragraphs = document.getElementsByTagName("p");
paragraphs.item(0).webkitMatchesSelector("p"); // true
paragraphs.item(1).webkitMatchesSelector("div#main > p"); // <div id="main"><p>a</p></div>などの場合はture
paragraphs.item(2).webkitMatchesSelector("html > head"); // false

のように、ドキュメント全体から見て要素がセレクタにマッチしているかをBooleanで返してくれる。

で、使いどころ。例えば、

<!doctype html>
<html>
<head><title>foo</title></head>
<body>
  <p>hello <a href="http://example.com/">example</a>.</p>
</body>
</html>

のようなHTMLで、p > aをクリックした時にアラートを出す場合。
昔だと、

var anchors = document.getElementsByTagName("a");
var i;
for (i = 0; i < anchors.length; i++) {
  if (anchors.item(i).parentNode.nodeType == Node.ELEMENT_NODE &&
      anchors.item(i).parentNode.tagName.toLowerCase() == "p") {
    anchors.item(i).addEventListener("click", function (event) {
      confirm("本当に飛びますか?") || event.preventDefault();
    }, false);
  }
}

こんな感じになっていたと思う。
で、querySelector系のAPIが入ってからは、

var anchors = document.querySelectorAll("p > a");
var i;
for (i = 0; i < anchors.length; i++) {
  anchros.item(i).addEventListener("cilck", function (event) {
    confirm("本当に飛びますか?") || event.preventDefault();
  }, false);
}

こういう風になって、
matchesSelectorが入ってからは、

document.addEventListener("click", function (event) {
  if (!event.target.webkitMatchesSelector("p > a"))
    return;
  confirm("本当に飛びますか?") || event.preventDefault();
}, false);

このように使えるようになった。
matchesSelectorが入ってからは、もはやanchorsを走査して、個々にイベントハンドラを設定するような事は必要なく、イベントのバブリングを使って、クリック時に簡単にふるい分けられるようになった。
これによって、対象ノードが大量にある場合にメモリと、イベントハンドラ設定時の速度を節約できるようになるだけでなく、動的にdocumentに追加された要素に対しても自動的にclickイベントをキャッチできるようになった(ただし、mousemove等の頻繁に呼ばれるイベントハンドラの場合、動的なチェックが逆に速度を遅くする事もあるので、注意しなければならない)。
後者は特に重要で、Ajax等を使ったプログラミングにおいて、動的に追加したノードのイベントハンドラを再設定したり、innerHTMLに代入したから再度イベントハンドラを設定しなければ、などという事を考えなくて済むようになる。

ところで、上で述べたテクニックはライブラリを使えば今すぐにでも使える技術である。
例えばjQuery#liveが使える。上級レベルの jQueryの説明が詳しい。
他には、Fast Ladderがこのテクニックを使っていて、matchesSelector相当の関数のqueryCSSという関数を使って実現している。

あと、SelectorsAPI 2にはqueryScopedSelectorやqueryScopedSelectorAllというメソッドが新しく作られたようだけれど、これについてはまだあんまり調べてない。

参考にしたページ:
本の虫: matchesSelectorについて