Node.compareDocumentPositionが素晴らしい

と呟いていたら、 id:caisui さんが
と教えてくれた。

広範囲にイベントを取得して、イベント発生元がどの要素に含まれるかで処理を変えたい時があって、そんな時は親ノードを辿って調べるという面倒なことをしていた。これとはおさらばしたいということで、compareDocumentPositionについて調べてみた。

The return value is a bitmask with the following values:

DOCUMENT_POSITION_DISCONNECTED = 0x01;
DOCUMENT_POSITION_PRECEDING = 0x02;
DOCUMENT_POSITION_FOLLOWING = 0x04;
DOCUMENT_POSITION_CONTAINS = 0x08;
DOCUMENT_POSITION_CONTAINED_BY = 0x10;
Node.compareDocumentPosition - MDC Doc Center

引用の通り、比較するとビットの合計値が返ってくる。
また、ドキュメント上に載っていないけど、DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20ってのもある。

比較ノードをa、比較対象をbとしてa.compareDocumentPosition(b)をすると以下の様な結果になる。

aとbの関係 返り値
同じノード 0
a の子孫に b 20((Node.DOCUMENT_POSITION_FOLLOWING Node.DOCUMENT_POSITION_CONTAINED_BY))
a の祖先に b 10((Node.DOCUMENT_POSITION_PRECEDING Node.DOCUMENT_POSITION_CONTAINS))
a の兄弟(弟) に b 4*1
a の兄弟(兄) に b 2*2
同一ドキュメントにない 32以上((Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC Node.DOCUMENT_POSITION_DISCONNECTED ??? 実装によって異なる))

表だとちょっと分かりにくいので図にすると

ってことで、以下の様な感じでコードが書けそう。

var elm = document.getElementById("elm_1");
window.addEventListener("click", function (event) {
  var res = elm.compareDocumentPosition(event.target);
  if (res == 0 || res & Node.DOCUMENT_POSITION_CONTAINED_BY) {
    // elmまたはelm内のノードがクリックされた時の処理
  }
}, false);

*1:Node.DOCUMENT_POSITION_FOLLOWING

*2:Node.DOCUMENT_POSITION_PRECEDING