部屋とYシャツと私みたいなタイトルになった・・。
わかったのかわかってないのか、とりあえず調べたことをメモっておくための記事。
事の発端
とある偉人のコードを見てた時に見つけた以下の行。
var DOCUMENT_POSITION_ANCESTOR = global.Node.DOCUMENT_POSITION_PRECEDING | global.Node.DOCUMENT_POSITION_CONTAINS; var DOCUMENT_POSITION_DESCENDANT = global.Node.DOCUMENT_POSITION_FOLLOWING | global.Node.DOCUMENT_POSITION_CONTAINED_BY;
どゆこと・・?
もしかして || って書こうとしてて忘れたんかな・・・って、
まあそんなわけ万が一にもないですよねー、ということで、コイツの正体を追っていくことに。
コレで何してるか
もちろん変数にとってあるので使う目的があるはず。
それを見てみると・・・、
pos = this.target.compareDocumentPosition(touchInfo.target); if (pos === DOCUMENT_POSITION_ANCESTOR || pos === DOCUMENT_POSITION_DESCENDANT) { // ... }
ふむ。
・・ふむ。
node.compareDocumentPosition(otherNode)とは
参考: Node.compareDocumentPosition() - Web API Interfaces | MDN
これはなにかというと、
var head = document.getElementsByTagName('head').item(0); head.compareDocumentPosition(document.body); // => 4
というように、ノード間の位置関係を取得するメソッド。
上記の例でいうと、head と body はHTMLの構造としては以下で、
<html> <head></head> <body></body> </html>
返ってきた 4 は、DOCUMENT_POSITION_FOLLOWING の 4 らしく、
つまり、head からすると body は、後にある(=FOLLOWING)関係ってことになる。
The return value is a bitmask with the following values:
さっきの 4 についてもっと詳しく。
compareDocumentPositionの返り値は以下で、ビットマスクであるらしい。
a.compareDocumentPosition(b)のとき、
name | value | 意訳 |
---|---|---|
DOCUMENT_POSITION_DISCONNECTED | 1 | bはaと同一tree上にない |
DOCUMENT_POSITION_PRECEDING | 2 | bはaの前にある |
DOCUMENT_POSITION_FOLLOWING | 4 | bはaの後にある |
DOCUMENT_POSITION_CONTAINS | 8 | bはaを含んでいる |
DOCUMENT_POSITION_CONTAINED_BY | 16 | bはaに含まれている |
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | 32 | 実装次第(なんだと...) |
さて、さっきの例では 4 が返ってきたけども、以下の例だと違う。
var head = document.getElementsByTagName('head').item(0); var title = document.getElementsByTagName('title').item(0); head.compareDocumentPosition(title); // => 20
これはHTML構造でみると、
<html> <head> <title></title> </head> </html>
さっきの表とてらしてぱっと見る感じ、
- titleはheadより、後ろにある
- titleはheadに、含まれている
なので、20。
えーっと、それはなんで?ってことで次。
ビットマスクとビット演算と
さっきの返り値 20 は、10進数の 20 。
ただコレはビットマスクで表した結果であると書いてありました。
ビットマスクについてはまず以下の記事を。
2進数で1|0のフラグを複数組み合わせて単一の条件を表現するって感じかね。
さっきの例でいうと、
// この結果が 20 head.compareDocumentPosition(title); // => 20 // 20はビットマスクで20なので、 // どういうビットの並びかを調べるには、2進数にする (20).toString(2); // => 10100 // 10100は、10000 と 100 から成るビットマスクってことがわかったので、 // 10000 を10進数に戻す parseInt(10000, 2); // => 16 // 100 も10進数に戻す parseInt(100, 2); // => 4
さっきの表でみると
- 4: DOCUMENT_POSITION_FOLLOWING (=後にある)
- 16: DOCUMENT_POSITION_CONTAINED_BY (=含まれている)
なので、最初の予想と同じになる、って感じ。
ビット演算の | は
a | b でどっちかが 1 なら 1 になるので、
var FLAG_A = 1; // 0001 var FLAG_B = 2; // 0010 var FLAG_C = 4; // 0100 var FLAG_D = 8; // 1000 // ----------------------- // AかBかC ------> 0111 var mask = FLAG_A | FLAG_B | FLAG_C; // => 0111なので 7
となる。
ここで最初のコードに
var DOCUMENT_POSITION_ANCESTOR = global.Node.DOCUMENT_POSITION_PRECEDING | global.Node.DOCUMENT_POSITION_CONTAINS; var DOCUMENT_POSITION_DESCENDANT = global.Node.DOCUMENT_POSITION_FOLLOWING | global.Node.DOCUMENT_POSITION_CONTAINED_BY;
さっきの表とてらしてみる
var DOCUMENT_POSITION_ANCESTOR = 2 | 8; // => 10 var DOCUMENT_POSITION_DESCENDANT = 4 | 16; // => 20
つまり、このコードの意味は、
pos = this.target.compareDocumentPosition(touchInfo.target); if (pos === 10 || pos === 20) { // ... }
ここでいう pos が、
- 4: DOCUMENT_POSITION_FOLLOWING (=後にある)
- 16: DOCUMENT_POSITION_CONTAINED_BY (=含まれている)
の組み合わせで 20 のとき
もしくは、
- 2: DOCUMENT_POSITION_PRECEDING (=前にある)
- 8: DOCUMENT_POSITION_CONTAINS (=含んでいる)
の組み合わせで 10 のとき
っていう判別をしていることになる・・と。
あーそーゆことね、完全に理解した!