八章第一回 Rangeとは
このページの最終更新日:
八章では、Rangeというものを解説します。今回は、そもそもRangeとはどういうものかを解説します。
Rangeとは
Rangeとは、文書中における「範囲」を表すものです。例えば、
あい<strong>うえ</strong>お。
というソースががあれば、
あいうえお。
のように表示されます。このとき、例えば
あいう えお 。
↑ ↑
ここから ここまで
のような感じである範囲を表せます。
要するに「『え』から『お』まで」なので、案外単純なように見えます。しかし、DOMという観点からこれを見ていくと、上の木構造は
- #text
"あい"
- strong
- #text
"うえ"
- #text
- #text
"お。"
となっていて、この範囲は(木構造上で)離れた複数のノードにまたがっています。こうなると、一筋縄ではいかないのが想像がつくと思います。このようなものを扱うのが、Rangeなのです。
範囲の表し方
Rangeは「開始点」と「終了点」で表します。今回の例の場合は、開始点は「『え』の前」、終了点は「『。』の前」ということになりますね。
Rangeでは、開始点や終了点はコンテナとオフセットを使って表されます。
これらの意味は、実は、文字列を表す、テキストノードなどの場合と、それ以外の場合に分けられます。まず、それ以外の場合について見ていきます。例えば、
- BBB
- CCC
- DDD
という木構造があり、AAAの子ノードたちの間に開始点があったとします。このとき、開始点と成り得る場所は、
- ←BBBの前
- BBB
- ←BBBとCCCの間
- CCC
- ←CCCとDDDの間
- DDD
- ←DDDの後
が考えられます。このように、文字列中の位置だけでなく、兄弟ノードにおいての、ノードとノードの間の位置も点と成り得ます。
さて、このとき、コンテナは、これらの兄弟の親ノードです。今回の場合、AAAですね。そして、オフセットは数値です。これは、最初の子ノードの前を0として、その次を1,その次を2,・・・と順番に番号をつけていったものです。つまり、
- ← 0
- BBB
- ← 1
- CCC
- ← 2
- DDD
- ← 3
ということです。ノードとノードの間の位置は必ずこのように表せます。
次に、文字列中の位置(文字と文字の間)の表し方を解説します。
- #text
"あいうえお"
- #text
"かきくけこ"
という木構造があって、例えば"あいうえお"
のテキストノードの中に点をおきたいとします。このとき、コンテナはそのテキストノードです。つまり、
- #text
"あいうえお"
←これ - #text
"かきくけこ"
ですね。
このとき、その文字列の中でも、
あ い う え お
↑ ↑ ↑ ↑ ↑ ↑
0 1 2 3 4 5
これだけ可能性があります。数字が振ってありますが、これがそのままオフセットになります。つまり、一番最初の位置が0、その次が1,・・・という具合ですね。
これで開始点や終了点をコンテナ・オフセットを使って表す方法が分かりました。では、前の例のRangeを実際に表してみましょう。
- #text
"あい"
- strong
- #text
"うえ"
- #text
- #text
"お。"
この例では、Rangeの範囲は「えお」の部分でした。まず、開始点のコンテナは
- #text
"あい"
- strong
- #text
"うえ"
←これ
- #text
- #text
"お。"
で、オフセットは1です。
と考えると、オフセットは1であることが分かります。
同様に、終了点は、コンテナが
- #text
"あい"
- strong
- #text
"うえ"
- #text
- #text
"お。"
←これ
で、オフセットは1ですね。
Rangeの作り方
これで、Rangeの範囲の表し方が分かりました。いよいよ、JavaScriptでのRangeの作り方を解説します。
Rangeを作るには、documentの持つメソッドcreateRangeを使います。引数はなく、戻り値で新しくできたRangeが帰ってきます。
var r = document.createRange();
こうしてできたRangeは、初期状態では開始点と終了点が共に「文書の一番前」に設定されています。コンテナとオフセットで表すと、コンテナはdocumentで、オフセットは0ということになっています。つまり、
- ←開始点・終了点
- html
- head
- …
- body
- …
- head
ですね。
また、もうひとつのRangeの作り方としてnewを使う方法があります。つまり、var r = new Range();
ということです。
ただRangeを作っても、開始点や終了点がまともでなければ使い物になりません。そこで、開始点・終了点を変更するメソッドがあります。それぞれ、setStartとsetEndです。
どちらも、引数は2つです。一つ目はコンテナ、二つ目がオフセットです。
var r = document.createRange();
r.setStart(document.body,0);
r.setEnd(document.body,document.body.childNodes.length);
という感じで使います。ちなみに、このソースでやっていることは分かりますか? 開始点は、body要素の一番最初に設定しています。終了点のコンテナも同じbody要素ですが、オフセットがdocument.body.childNodes.lengthとなっています。
これはどういうことかというと、あるノードの最初の子ノードを0番目の子ノードとしたとき、
- ←オフセット: 0
- BBB …0番目
- ←オフセット: 1
- CCC …1番目
- ←オフセット: 2
- DDD …2番目
- ←オフセット: 3
となっていて、オフセットがnの位置は、n番目の子ノードの直前になっています。ここで、childNodes.lengthは子ノードの数なので、一番最後の子ノードは(length-1)番目ということになります。つまり、childNodes.length
を指定することで、その要素の一番後ろに位置を設定していたのです。
結局、このRangeは、body要素の中身全体を包んでいるということになります。
なお、開始点が終了点より後ろになるように設定したり、終了点が開始点より前になるように設定することもできません。もしそのように設定しようとした場合、例えば開始点が終了点より後ろになるようにしようとした場合は、終了点もその場所まで押し下げられます。逆も然りです。ちなみに、このように、開始点と終了点が一致しているRangeを「つぶれている」(collapsed)というそうです。
だから、上のコードでは、createRangeで作ったノードは開始点と終了点が共に文書の一番前だからつぶれているRangeで、そのあとsetStartで開始点をbody要素の最初に設定しようとしたとき、終了点より開始点が後になってしまうから、一旦終了点もbody要素の最初の位置にきていたのです。その後setEndで終了点のみbody要素の最後の位置になります。
また、現在の開始点や終了点を知る方法もあります。これらは、Rangeのプロパティとして参照できます。startContainer,startOffset,endContainer,endOffsetの4つのプロパティをRangeは持っており、それぞれ開始点のコンテナ、開始点のオフセット、終了点のコンテナ、終了点のオフセットです。
ただし、これらは書き換えできません。これを直接いじって開始点・終了点を変えることはできないのです。
その他のメソッド・プロパティ
他にもいろいろRangeをいじるメソッドがあります。
setStartBefore,setEndBeforeは、それぞれ開始点・終了点を、あるノードの直前に移動します。引数はそのノードです。例えば、
var r = document.createRange();
r.setStartBefore(document.body);
とした場合、body要素の前だから、開始点は
- html ←コンテナ
- head
- ←開始点
- body
ということになります。即ち、コンテナはhtml要素で、オフセットは1ですね。
また、setStartAfter,setEndAfterというメソッドもあります。これは、上の2つは逆にあるノードの直後に移動します。
さらに、使う機会があるかは知りませんが、collapseというメソッドがあります。これは、そのRangeをつぶれた状態にする、つまり開始点と終了点が同じ状態にするものです。このとき、開始点に合わせるか終了点に合わせるかという問題があるので、それを1つの引数で指定します。これは真偽値で、trueなら開始点、falseなら終了点に合わされます。
また、そのRangeが今つぶれているかどうかは、collapsedというプロパティに真偽値で入っています。
さらにさらに、selectNodeとselectNodeContentsという2つのメソッドがあります。どちらも引数はノード一つです。これらのメソッドでは、開始点と終了点が同時に変更されます。
selectNodeは、Rangeを、そのノードを囲む(選択した)状態にするというものです。例えば、
var r = document.createRange();
r.selectNode(document.body);
としたとき、
- html
- head
- ←開始点
- body
- ←終了点
となります。body要素全体が範囲になっていることがわかります。開始点と終了点のコンテナはいずれもhtml要素で、開始点はオフセット1、終了点はオフセット2となります。
selectNodeContentsは、そのノードの外側から囲むのではなく、内側から囲みます。つまり、例えば
var r = document.createRange();
r.selectNodeContents(document.body);
としたときは、
- html
- head
- body ←コンテナ
- ←開始点
- …
- …
- …
- ←終了点
という感じになります。コンテナはどちらもbody要素になります。
今回は、Rangeの作り方を解説しました。次回から、Rangeの使い方を解説していきます。