テキストハイライトの主流なアプローチ

テキストハイライトの主流なアプローチ


テキストハイライトは一見シンプルに見えます—背景を黄色に変えるだけ—ですが、実際はかなり複雑です。下の例のように、ハイライトされた部分が複数の要素にまたがり、下の要素の一部のテキストも含んでいます。

Highlights across elements

以下では、テキストハイライトを実装するためのいくつかの一般的なアプローチを紹介し、それぞれの利点と欠点、実際の使用例を議論して、主流な技術的解決策を整理します。

CSS Highlight API — 現代的で効率的なテキストハイライト

これは現在テキストハイライトを実装するための最良の選択肢で、使いやすく高性能です。唯一の欠点は、ブラウザが比較的新しいバージョンをサポートする必要があることですが、2025年の現在ではすべての主要ブラウザがサポートしています。

Highlight オブジェクト

まず、各ハイライト領域に対して Range オブジェクトを作成する必要があります。Range は長年存在するAPIで、DOMツリー上の連続した領域を表します—よくわからない場合は、自分で勉強してください。

const range = new Range();
range.setStart(someNode, startOffset);
range.setEnd(someNode, endOffset);

次に、Highlight オブジェクトに追加します。ハイライトは複数の非連続領域を持つことができるため、1つの Highlight オブジェクトは複数のRangeを含むことができます。

const highlight = new Highlight(range1, range2, ..., rangeN);

これはSetのようなオブジェクトなので、adddelete などのメソッドを使用して含まれるRangeを変更できます。

CSS.highlights

次に、Highlight オブジェクトを CSS.highlights に追加します。これはMapのようなオブジェクトで、getsetdelete などのメンバーの追加・削除メソッドと、同じ反復メソッドを持っています。

Mapと同様に、Highlightを追加する際には、ハイライトの名前としてキーを指定する必要があります。この名前は非常に有用です—以下で見るように、この名前を使用してハイライトのスタイルを設定します。例えば緑色の背景などです。したがって、同じHighlightオブジェクト内のすべてのハイライトは同じスタイルを持ちます。別のテキストを青色でハイライトしたい場合は、新しいハイライトを作成する必要があります。

CSS.highlights.set("my-highlight", highlight);

疑似要素 ::highlight の定義

次に、CSSスタイルが登場します。::highlight(ハイライト名) 疑似要素を使用してハイライトスタイルを定義できます。

::highlight(my-highlight) {
  background-color: yellow;
  color: black;
}

注意: 使用できるCSSプロパティは少数のみです。background-colorcolorcursor など。詳細は仕様を参照してください。

ハイライト範囲の動的変更

CSS.highlights はMapのようなもので、Highlightを更新するための類似したメソッドがあります:

  • CSS.highlights.clear()
  • set(highlightName, Highlight)
  • delete(highlightName)

Highlight オブジェクトはSetのようなもので、Rangeを更新するための類似したメソッドがあります:

  • Highlight.add(range)
  • delete(range)

テキスト要素のスタイルを変更

DOMを走査してターゲットテキストノードを見つけ、その背景スタイルを直接変更してハイライトを実現します。

  • DOMの変更が必要で、ブラウザのリフローが発生し、パフォーマンスのオーバーヘッドが大きくなります。
  • 要素間ハイライト(ハイライト範囲が複数の要素にまたがる)の実装はかなり複雑です。 Highlights across elements
  • ハイライトされたコンテンツが比較的固定されており、範囲が小さいシナリオに適しています。

テキストDOMを変更せずにオーバーレイでハイライトをレンダリング

透明なオーバーレイレイヤーを作成し、ターゲットテキストの画面位置を計算して、オーバーレイに半透明のハイライトを描画します。

ハイライト領域の位置決め

  • Element.getClientRects() または getBoundingClientRect() を使用して、ページ上のテキスト座標を取得します。
  • 複数行テキストを処理する場合、各行は個別のハイライトブロックが必要な矩形領域に対応します。
  • ウィンドウサイズの変更やフォントサイズの変更により、ハイライト位置の再計算が必要になり、パフォーマンスに影響する可能性があります。

ハイライトの描画方法

DIV をオーバーレイとして使用

絶対配置された親コンテナと半透明背景の子要素を使用してテキストをオーバーレイします。 例えば、Monacoエディタはこのアプローチを使用して同じ単語ハイライトを実装しています。

<div class="editor" style="position: relative;">
  <div class="overlay" style="position: absolute; height: 0;">
    <div
      class="highlight"
      style="top:0; left:57px; width:23px; background-color: rgba(173,214,255,0.15);"
    ></div>
    <!-- より多くのハイライト領域を追加 -->
  </div>
  <div class="content">
    <!-- テキストコンテンツ -->
  </div>
</div>
  • オーバーレイの位置はabsoluteで、親要素が position: static でないことを確認して、absolute配置が機能するようにします。これにより、内部のハイライト要素を任意の場所に配置できます。
  • テキストの上に配置するには、HTMLでオーバーレイをテキストコンテナの前に配置するだけです。オーバーレイの高さは0で、下のテキストとの相互作用に影響しないようにします。
  • 内部に要素を追加し、background-color を半透明のハイライト色に設定し、ハイライトされたテキストの位置に配置し、幅を設定します。
  • Monacoエディタ(VSCodeはこれに基づいています)はこの方法を使用します。公式デモを参照してください。キーワードを選択すると、同じキーワードがハイライトされます。HTMLを見ると、これらのキーワードはスタイルを変更していませんが、エディタインターフェースにオーバーレイがあり、内部に半透明のハイライト色が描画されています(class="view-overlays" のdivを参照)。

SVG をオーバーレイとして使用

  • オーバーレイをページと同じ寸法に設定
  • 内部に <rect> を追加し、ハイライトされたテキストの位置に描画
  • foliate-js はこの方法を使用します。overlayer.js を参照してください。

Selection API と ::selection 疑似要素を使用 — ブラウザのデフォルトテキスト選択ハイライト

ブラウザは自然に選択されたテキストをハイライトするため、選択を制御することで間接的にハイライトを実現できます。スタイルはCSSの ::selection 疑似要素でカスタマイズできます。

const range = new Range();
range.setStart(parentNode, startOffset);
range.setEnd(parentNode, endOffset);

// 選択を設定
const selection = document.getSelection();
selection.removeAllRanges();
selection.addRange(range);
::selection {
  background-color: #c3c2c2;
  color: white;
}

この方法はかなり巧妙で、シンプルで、広いブラウザサポートがありますが、大きな欠点があります:同時に1つの選択しか存在できないため、プログラム的な選択と実際のユーザー選択が競合し、互いに上書きされます。したがって、以下の条件を満たすシナリオにのみ適しています:

  • ユーザーがテキストを選択できない
  • 連続したハイライトされたテキストセグメントが1つだけある
  • 古いブラウザをサポートする必要がある
  • 怠けて他の方法を避けたい