uhyohyo.net

JavaScript初級者から中級者になろう

八章第二回 Rangeの機能

前回は、Rangeの作り方を解説しました。今回は、Rangeを使ってできることを解説します。

deleteContents

まず、Rangeが持つdeleteContentsというメソッドを解説します。これは、Rangeの示す範囲にあるものを残さず削除するというものです。引数も戻り値もありません。例えば、

AAA
|
├――BBB
|  |    ←開始点
|  └――CCC
|
├――DDD
|       ←終了点
└――EEE
          

というRangeがあったとき、deleteContentsをすると

AAA
|
├――BBB
|     ←開始点
|     ←終了点
└――EEE
          

という状態になります。間の部分が見事に消えました。このとき、開始点と終了点の間がすべてなくなったので、当然このRangeの開始点と終了点は同じに(つぶれた状態に)なります。

また、今回の場合点がノードとノードの間だったので簡単ですが、テキストの途中の場合でも同じです。

AAA
|
├――BBB
|  |
|  └――#text "aaa|bbb"
|           ↑開始点
├――DDD
|
└――#text  "ccccc|dd"
           ↑終了点
          

という範囲の場合、

AAA
|
├――BBB
|  |
|  └――#text "aaa"
|
└――#text  "dd"
          

のようにうまく処理されます。

cloneContents

さて、今度はcloneContentsというメソッドがあります。これは、選択部分をまるまるコピーしてその部分だけ得るというメソッドです。戻り値で結果が得られます。例えば、

AAA
|       ←開始点
├――BBB
|  |
|  └――CCC
|
├――DDD
|       ←終了点
└――EEE
          

というRangeでcloneContentsした場合、

├――BBB
|  |
|  └――CCC
|
└――DDD
          

という部分を取得できます。メソッドの名前の通りあくまで「クローン」なので、選択されているほうには変化はありません

もちろん、テキストでもOKです。

AAA
|
├――BBB
|  |
|  └――#text "aaa|bbb"
|          ↑開始点
├――DDD
|
└――#text  "ccccc|dd"
          ↑終了点
          

の場合は、

|
├――BBB
|  |
|  └――#text "bbb"
|
├――DDD
|
└――#text  "ccccc"
          

というものを取得できます。わかりにくければ、タグで表わしてみるといいかもしれません。

<AAA><BBB>aaabbb</BBB><DDD />cccccdd</AAA>

というようになっていて、強調部分が選択範囲です。これを抜き出すと

bbb</BBB><DDD />ccccc

となります。ここで、最初のbbbはもともとBBBタグの中にあったから、

<BBB>bbb</BBB><DDD />ccccc

のようにうまいこと補います。こうすると、上の木構造の図と同じになりました。

さて、木構造の一部ですから、取得した部分もまたこのような構造を持っています。しかし、これらはよく見ると親がありません。そういうとき、どうするのでしょう。実は、ここで七章第四回documentFragmentが出てきます。つまり、

DocumentFragment
|
├――BBB
|  |
|  └――#text "bbb"
|
├――DDD
|
└――#text  "ccccc"
        

ということです。cloneContentsの戻り値はDocumentFragmentだったのです。

また、ここで、サンプルで「全体がAAAタグに囲まれていたのだから、最後にAAAタグが補われるのではないか」と考えた人もいると思います。しかし、あるタグの完全に内側の範囲でcloneContentsした場合、そこより外側は考慮されないことになっています。もし、範囲がAAAの外にまでわたっていれば、AAAの中と外を区別する必要があるので、AAAも返り値に出てきます。

extractContents

さて、次に解説するextractContentsは、上の2つを合わせたようなメソッドです。どういうことかというと、このメソッドは、そのRangeの範囲の部分を抜き出して、それを返すというものです。抜き出すとは、つまり木構造から除去するということです。これは、deleteContentsと同じですね。そして、その部分を戻り値として得ることが同時にできるのです。つまり、cloneContentsをしたときと同じ戻り値を得ることができます。使うときに使えば結構便利かもしれません。

compareBoundaryPoints

今度は、compareBoundaryPointsを解説します。これは、2つのRangeの位置を比較するというものです。

range.compareBoundaryPoints(how, sourceRange)

というように使います。引数が2つありますね。rangeとsourceRangeの2つが、比較するRangeです。さて、もう一つあるhowという引数は定数です。これは、比較の仕方を表すものです。

というのも、二つのRangeを比較するさい、基準にする点を「開始点」にする方法と「終了点」にする方法の二種類あります。そこで、これをどうするかを定数で決めるのです。

定数の説明に移る前に、このメソッドの比較の仕方ですが、「sourceRangeから見たrangeの位置」を、-1,0,1のいずれかの数で返します。rangeがsourceRangeより前なら-1、位置が同じなら0、後なら1が返ります。

さて、定数の説明ですが、定数は4種類あります。

START_TO_START
開始点どうしを比べます。
START_TO_END
sourceRangeの開始点と、rangeの終了点を比べます。
END_TO_START
sourceRangeの終了点と、rangeの開始点を比べます。
END_TO_END
終了点どうしを比べます。

これらの定数は、Rangeオブジェクトのプロパティとして使用できます。

Range.START_TO_START

という感じで使います。

例えば、

range      : |------|
            abcdefghijklmnopqrstuvwxyz
sourceRange:             |-------|
          

というように、rangeがsourceRangeより完全に前にあるのを判定するには、どうすればいいでしょう。

range.compareBoundaryPoints(Range.START_TO_END,sourceRange) <= 0

というようにします。

START_TO_ENDで、

range      : |------| ←rangeの終了点
            abcdefghijklmnopqrstuvwxyz
sourceRange:             |-------|
   sourceRangeの開始点↑
          

の2つを比べます。rangeの終了点がsourceRangeの開始点からみて前にあればいいので、-1ということになります。ところが、上をみると、「0以下」なので-1のほか0でもいいことになります。これは、0(位置が同じ)場合にも、完全に前にあるとみなせるということです。そう見なしたくなければ、-1のみで判定するようにしましょう。

また、比較が2回以上必要な場合もあります。例えば、

range      : |---------------|
            abcdefghijklmnopqrstuvwxyz
sourceRange:     |-------|
          

のように、sourceRangeがrangeに完全に囲まれている場合を判定したい場合は、「rangeの開始点がsourceRangeの開始点より前」「rangeの終了点がsourceRangeの終了点より後」という2つの条件を満たす必要があります。これをJavaScriptで表すには、論理積演算子の&&七章第二回)を使います。

range.compareBoundaryPoints(Range.START_TO_START, sourceRange)  <= 0 && range.compareBoundaryPoints(Range.END_TO_END, sourceRange) >= 0

というようになりますね。そのほか、自分で必要に応じてやってみましょう。

insertNode

さて、まだまだ解説します。次はinsertNodeです。これは、ノードを引数にとり、そのRangeの開始点の位置にノードを挿入するということです。例えば、

 |-----|
abcdefghijklmn
          

というRangeがあり、この開始点に

<AAA>123</AAA>

というノードを追加したとき、結果は

a<AAA>123</AAA>bcdefghijklmn

となります。

これはこのようにタグで見ると単純な動作ですが、木構造で見ると

|
└――#text "abcdefghijklmn"
          

|
├――#text "a"
|
├――AAA
|   |
|   └――#text "123"
|
└――#text "bcdefghijklmn"
          

というようになり、もとのテキストノードが2つに分断されています。これは、多少面倒ですよね。これをRangeを使って簡単にできるというのは、なかなかいい点です。

ただし、この場合Rangeの表す範囲は関係なく、開始点だけが関わっているので、Rangeのメソッドとしてはちょっと特殊です。

cloneRange

cloneRangeは、その名の通りRangeを複製するメソッドです。返り値は新しくできたRangeで、引数はありません。

toString

toStringは、Rangeの範囲を全てテキストにして返すメソッドです。引数はありません。

 |---------|
abcde<em>fghij</em>klmn
          

というRangeならば、返り値は"bcdefgh"ということになります。

detach

detachは、そのRangeを破棄するメソッドです。破棄というのは、そのRangeを使えない状態にするということです。

そもそもRangeは、ある目的に使い終わったらまた開始点や終了点を再設定して使いまわすことができます。しかし、このメソッドを使用した場合、もうそのRangeは使うことができません。

わざわざそんなことをする意義は、おそらくメモリ解放などでしょう。Range1つ程度で大した差はないのではとも思いますが。

実際よく分かりませんが、とりあえずあるのだから使いましょう。Rangeを使い終わったら、そのRangeをdetachするとよいです。引数も返り値もありません。