AbstractRange

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2021.

AbstractRange は抽象インターフェイスで、すべての DOM の範囲型を定義するための基本クラスです。 range は、文書内のコンテンツの区間の開始点と終了点を示すオブジェクトです。

メモ: 抽象インターフェイスなので、 AbstractRange タイプのオブジェクトを直接インスタンス化することはありません。代わりに Range または StaticRange インターフェイスを使用してください。これら 2 つのインターフェイスの異なる点を理解し、自分のニーズに合ったものを選ぶには、それぞれのインターフェイスのドキュメントを参照してください。

インスタンスプロパティ

collapsed 読取専用

論理値で、範囲が折りたたまれている場合は true となります。折りたたまれた範囲とは、開始位置と末尾位置が同じで、 0 文字の長さの範囲です。

endContainer 読取専用

endOffset プロパティで指定された範囲の末尾がある場所の Node オブジェクトです。

endOffset 読取専用

ノードの内容の先頭から、この範囲オブジェクトで表す範囲の先頭までのオフセットを文字数で表した整数値です。この値は endContainer ノードの長さよりも小さくなければなりません。

startContainer 読取専用

startOffset プロパティで指定された範囲の先頭がある場所の Node オブジェクトです。

startOffset 読取専用

ノードの内容の先頭から、範囲オブジェクトが参照する内容の先頭の文字までのオフセットを文字数で表した整数値です。この値は startContainer で示すノードの長さよりも小さくなければなりません。

インスタンスメソッド

AbstractRange メソッドを提供していません。

使用上の注意

Range 型

文書内のコンテンツの範囲は、すべて AbstractRange に基づくインターフェイスのインスタンスを使用して記述します。このようなインターフェイスは 2 つあります。

Range

Range インターフェイスは長い間存在していましたが、他にも範囲データを定義する必要性が生じたため、最近になって AbstractRange をベースに再定義されました。 Range は範囲の端点を変更するメソッドや、範囲を比較するメソッド、範囲間の交差を検出するメソッドなどを提供します。

StaticRange

StaticRange は一度作成した範囲を変更することができない基本的な範囲です。つまり、ノードツリーが変化しても範囲は変更されません。より複雑な Range インターフェイスのパフォーマンスやリソースへの影響を避けることができるので、一度だけ使用する範囲を指定する必要がある場合に有益です。

要素の内容

要素の内容にアクセスする場合、要素自体もノードですが、要素内のテキストもノードであることに注意してください。要素のテキスト内に範囲端点を設定するには、必ず要素内のテキストノードを探してください。

js
const startElem = document.querySelector("p");
const endElem = startElem.querySelector("span");
const range = document.createRange();

range.setStart(startElem, 0);
range.setEnd(endElem, endElem.childNodes[0].length / 2);
const contents = range.cloneContents();

document.body.appendChild(contents);

この例では、新しい範囲 range を作成し、その始点をクラスが elementclass である最初の要素の 3 つ目の子ノードに設定します。終点は span の最初の子の中央に設定し、その後、範囲のコンテンツをコピーするために使用しています。

範囲と DOM の階層構造

文書内の文字の範囲を定義する際に、 0 個以上のノード境界をまたぐことが可能で、 DOM の変化にできるだけ強い方法で定義するために、 HTML 内の最初の文字と最後の文字のオフセットを指定することはできません。それにはいくつか理由があります。

最初に、ページが読み込まれた後、ブラウザーは HTML の観点で考えていません。読み込まれた後のページは DOM の Node オブジェクトのツリーなので、範囲の先頭とあ末尾の位置をノードとノード内の位置で指定する必要があります。

2 つ目に、 DOM ツリーの変更可能性に可能な限り対応するために、文書全体の中でのグローバルな位置ではなく、ツリー内のノードとの相対的な位置を表す方法が必要です。文書内の点を指定されたノード内のオフセットとして定義すれば、 DOM ツリー内でノードが追加されたり除去されたり移動したりしても、その位置はコンテンツと矛盾しません。かなり明白な制限がありますが(ノードが範囲の終点の後に移動された場合や、ノードのコンテンツが大きく変更された場合など)、何もしないよりははるかにましです。

3 つ目に、ノード相対位置を使用して開始位置と終了位置を定義すると、一般的にパフォーマンスが向上しやすくなります。グローバルオフセットが何を参照しているのか DOM と交渉する必要がなく、ユーザーエージェント(ブラウザー)は開始位置で示されるノードに直接移動してそこから開始し、指定されたオフセットに達するまで終了ノードに進むことができます。

これを示すために、下記の HTML を考えてみましょう。

html
<div class="container">
  <div class="header">
    <img src="" class="sitelogo" />
    <h1>The Ultimate Website</h1>
  </div>
  <article>
    <section class="entry" id="entry1">
      <h2>Section 1: An interesting thing…</h2>
      <p>A <em>very</em> interesting thing happened on the way to the forum…</p>
      <aside class="callout">
        <h2>Aside</h2>
        <p>An interesting aside to share with you…</p>
      </aside>
    </section>
  </article>
  <pre id="log"></pre>
</div>

HTML を読み込んで文書の DOM 表現を構築すると、結果の DOM ツリーは次のようになります。

単純なウェブページの DOM 図

この図では、 HTML 要素を表すノードを緑色で表示しています。その下の各行は、 DOM ツリーの次の階層を表示させます。青いノードはテキストノードで、画面に表示させるテキストを格納します。各要素のコンテンツはツリーの下記でリンクされており、要素が他の要素やテキストノードを含めるために、その下に一連の分岐を生む可能性があります。

もし <p> 要素のコンテンツが "A <em>very</em> interesting thing happened on the way to the forum…" である範囲を作成したい場合は、次のようにします。

js
const pRange = document.createRange();
pRange.selectNodeContents(document.querySelector("#entry1 p"));

<p> 要素の子孫を含めるために、そのコンテンツ全体を選択したいので、これはうまく動作します。

代わりに <section> の見出し(h2 要素)から <em> の "ve "の終わりを通り、その下の段落に "An interesting thing…" というテキストをコピーしたい場合、以下のコードで動作します。

js
const range = document.createRange();
const startNode = document.querySelector("section h2").childNodes[0];
range.setStart(startNode, 11);

const endNode = document.querySelector("#entry1 p em").childNodes[0];
range.setEnd(endNode, 2);

const fragment = range.cloneContents();

ここで興味深い問題が発生します。DOM階層の異なるレベルにある複数のノードからコンテンツをキャプチャし、そのうちの一部だけをキャプチャしているのです。結果はどのように見ていけばいいのでしょうか?

幸いなことに、 DOM 仕様はこの課題に的確に対応しています。例えば、この例では cloneContents() を範囲に呼び出して、指定した範囲のコンテンツを複製する DOM サブツリーを提供する新しい