k8o

LLMS

Shadow DOM境界を跨いだ選択範囲の処理を可能にするgetComposedRanges

公開: 2025年8月24日(日)
更新: 2025年8月24日(日)

はじめに

SelectionオブジェクトのgetComposedRangesメソッドがBaseline 2025入りを果たしました。このメソッドは、Shadow DOMを含む文書全体での選択範囲を取得できるようにします。

この記事では、SelectionオブジェクトとそのgetComposedRangesメソッドについてサンプルを交えて解説します。

Selection

Selectionはユーザーが選択したテキストの範囲やキャレットの位置を扱うオブジェクトです。

window.getSelection()を使用して、現在の選択範囲を取得できます。

const selection = window.getSelection();

// 選択範囲の文字列を取得
const text = selection.toString();

Selectionオブジェクトのプロパティの紹介

サンプルテキスト

あさ、眼をさますときの気持は、面白い。かくれんぼのとき、押入れの真っ暗い中に、じっと、しゃがんで隠れていて、突然、でこちゃんに、がらっと襖をあけられ、日の光がどっと来て、でこちゃんに、「見つけた!」と大声で言われて、まぶしさ、それから、へんな間の悪さ、それから、胸がどきどきして、着物のまえを合せたりして、ちょっと、てれくさく、押入れから出て来て、急にむかむか腹立たしく、あの感じ、いや、ちがう、あの感じでもない、なんだか、もっとやりきれない。

選択中のテキスト(selection.toString()

選択要素の開始位置の要素

selection.anchorNode.textContent, selection.anchorOffset

選択要素の終了位置の要素

selection.focusNode.textContent, selection.focusOffset

選択の種類(selection.type

上記の例では、Selectionオブジェクトが持つ選択中の文字列の表示と、いくつかのプロパティを紹介しています。

この他にも、Selectionオブジェクトは選択中の範囲の個数を返すrangeCountプロパティや、テキストが選択されているかどうかを表すisCollapsedプロパティなどを持ちます。

テキストの選択範囲やキャレットの変更は、selectionchangeイベントによって検知することができます。

document.addEventListener('selectionchange', () => {
  const selection = window.getSelection();
  ...
});

documentに対して登録しているので、上の例はこのページ内のどこを選択しても動作します。

続いて、Selectionオブジェクトが持つメソッドの紹介です。

Selectionオブジェクトのメソッドの紹介

サンプルテキスト

あさ、眼をさますときの気持は、面白い。かくれんぼのとき、押入れの真っ暗い中に、じっと、しゃがんで隠れていて、突然、でこちゃんに、がらっと襖をあけられ、日の光がどっと来て、でこちゃんに、「見つけた!」と大声で言われて、まぶしさ、それから、へんな間の悪さ、それから、胸がどきどきして、着物のまえを合せたりして、ちょっと、てれくさく、押入れから出て来て、急にむかむか腹立たしく、あの感じ、いや、ちがう、あの感じでもない、なんだか、もっとやりきれない。

選択範囲の追加

selection.addRange(Range)

選択範囲の削除

selection.removeAllRanges(), selection.empty()

要素の子を全て選択する

selection.selectAllChildren(Node)

範囲の変更

selection.extend(Node, ?offset)

addRangeRangeオブジェクトを引数に取り、選択範囲に対象を追加します。 Firefox以外のブラウザでは複数の選択をサポートしていないです。addRangeを実行する前に、rangeCountプロパティを確認して1以上の場合は選択範囲をクリアする必要があるので注意してください。

extendは選択範囲から指定したノードの先頭までを選択範囲に拡張します。先頭以外の位置を指定したい場合は、オフセットを第2引数に渡します。

メソッドについても、プロパティと同様に、ここで紹介したもの以外に多数存在します。 しかし、この章の目的はSelectionオブジェクトの概要と基本的な機能を理解するところなので、説明はここまでとします。

getComposedRanges

getComposedRanges()はShadow DOM境界を跨いだ選択範囲の処理を可能にするメソッドです。

const ranges = getComposedRanges({ shadowRoots: [shadowRoot1, shadowRoot2] });

戻り値はStaticRangeオブジェクトの配列です。StaticRangeRangeと似ていますが、重要な違いがあります。RangeはDOMの変更に動的に追従するのに対し、StaticRangeは作成時点のDOM状態を固定的に保持します。

オプションにはShadowRootオブジェクトの配列を渡します。渡したShadowRootオブジェクトに含まれる要素であれば、Shadow DOM内の要素であっても選択範囲として検出されます。 選択範囲にオプションで指定していないShadowRootオブジェクトが持つ要素が含まれる場合は、そのShadow DOMのホスト要素が返されます。

getComposedRangesメソッドの紹介

サンプルテキスト(テキスト全体が閉じたShadow Tree)

あさ、眼をさますときの気持は、面白い。かくれんぼのとき 、押入れの真っ暗い中に、じっと、しゃがんで隠れていて、突然、でこちゃんに、がらっと襖をあけられ、日の光がどっと来て、でこちゃんに、「見つけた!」と大声で言われて、まぶしさ、それから、へんな間の悪さ、それから、胸がどきどきして、着物のまえを合せたりして、ちょっと、てれくさく、押入れから出て来て、急にむかむか腹立たしく、あの感じ、いや、ちがう、あの感じでもない、なんだか、もっとやりきれない。

SafariやIOSのChrome等ではoptionsを含んだgetComposedRangesメソッドが正しく動作しない場合があります。

これまで使われていたgetRangeAtメソッドと、getComposedRangesメソッドを使った時の比較、およびgetComposedRangesメソッドの引数にShadowRootを渡した場合と渡さなかった場合の違いを確認できます。

サンプルテキスト全体にattachShadowしています(closed)。

背景色があるテキスト部分を全て含めて選択した場合や、背景色があるテキストを一部含めた場合、サンプルテキスト以外も含めた選択を試してください。 getComposedRangesメソッドにオプションを渡した場合はstartContainerendContainerに正確な要素が渡りますが、それ以外はShadow Treeを含む要素全体が返されます(MacOSのChromeを用いた結果のため、他のブラウザでは異なる結果になるかもしれません)。

このように、適切なオプションを付与したgetComposedRangesメソッドを用いることで、Shadow DOM境界を跨いだ選択が行われたとしても、他の要素と同じようにShadow DOM内の要素を正しく扱えるようになります。

この記事はどうでしたか?

500文字以内でご記入ください

ブログの購読

k8oのブログを購読する

k8oのブログを購読することで、最新の情報を受け取ることができます。

登録いただいたメールアドレスは、購読のためにのみ使用されます。