おい、調子はどうか。
日々のCSS設計やパフォーマンスチューニングで頭を悩ませているところ申し訳ないが、今回はフロントエンドの表現力を一段階……いや、数段階引き上げる話をしよう。
テーマは `::highlight` 擬似要素によるカスタムハイライト だ。
おいおい、また新しいプロパティかよ、`background-color`や``タグじゃダメなのか?って思ったそこの君。大間違いだ。これまでの常識を根底から覆す、DOMを汚さないテキストハイライトの決定版なんだから、少し耳を貸してくれ。
実務で「ページ内検索のヒット部分を動的に光らせたい」「エディタのようなリッチなテキスト選択を作りたい」って要件、必ず一度はぶち当たるよな。その度に何でもかんでもJSでテキストノードを分割して``で囲って……ってやってなかったか?
DOMの構造をガチャガチャいじると、当然パフォーマンスは落ちるし、DOMの再構築コスト(リフロー・リペイント)は跳ね上がる。何より、テキストが更新されるたびにJS側のロジックが発狂しかける。
それをスマートに、かつ極限のパフォーマンスで解決してくれるのが、今回の主役である CSS Custom Highlight API と `::highlight()` 擬似要素のコンボ技だ。
さあ、現場のプロとしての知見を交えて、その実力と実装の裏側を徹底的に解説してやろう。
—
1. なぜDOMを汚してはいけないのか?(裏側のブラウザ処理)
まず、これまでのアプローチと何が違うのかを理解するために、ブラウザが画面を描画する裏側の話をしよう。
従来の `` や `` によるハイライトは、「DOMツリーを書き換える」 アプローチだ。
JavaScriptで文字列をパースし、テキストノードを細切れにして要素でラップする。これの何が辛いって、DOMのノード数が爆発的に増えることだ。長い文章でこれをやると、DOMツリーのメモリ消費量が増えるだけでなく、ちょっとしたテキストの変更でレイアウトツリー全体が再構築される(いわゆるリフローの嵐)。
一方、`Highlight API` はどう動くか?
JavaScript側で「ここからここまで」というテキストの範囲(`Range`オブジェクト)を指定し、それを `Highlight` オブジェクトとしてグループ化する。そして、それをブラウザのレンダリングエンジンに登録する。
つまり、DOMは1ミリも汚さない。
HTMLの構造はクリーンなテキストノードのまま、ブラウザの描画レイヤー(CSSのレイヤー)だけで「この範囲に背景色を引け」と命令するんだ。
これにより、ブラウザは余計なレイアウト計算をスキップし、ペイント(描画)のステージだけで高速にハイライトを処理できる。このアーキテクチャの美しさに、俺は初めて知ったとき思わずうなったね。
—
2. 実装の全体像:JavaScriptとCSSの連携プレイ
カスタムハイライトを実装するには、CSS単体では完結しない。JavaScript(CSS Custom Highlight API) と CSS(`::highlight` 擬似要素) の二刀流が必要になる。
役割分担はこうだ:
- JavaScript: 「どのテキストの、どこからどこまでをハイライトするか」という範囲(Range)を定義する。
- CSS: その範囲に対して「どう装飾するか(背景色や文字色など)」を定義する。
百聞は一見に如かず。実際に動くコードを見てみよう。
HTML
効率的かつ柔軟にテキストのハイライトを実現できます。
パフォーマンスの観点からも、非常に優れたアプローチです。
CSS
.article-body {
font-size: 1.1rem;
line-height: 1.8;
color: #333;
}
/
::highlight()擬似要素の定義
引数には、JS側で登録したハイライトの名前(カスタム名)を指定する
/
::highlight(search-results) {
background-color: #ffe066; / 鮮やかなイエローの背景色 /
color: #111; / テキストの文字色 /
border-radius: 2px; / ほんのり角丸にすることも可能 /
}
JavaScript
// 1. ハイライトしたい要素と、検索するキーワードを取得
const contentElement = document.getElementById(‘target-content’);
const searchText = ‘パフォーマンス’;
// 2. テキストノードから該当キーワードを探してRangeオブジェクトを作る関数
function createRanges(rootNode, keyword) {
const ranges = [];
const walker = document.createTreeWalker(rootNode, NodeFilter.SHOW_TEXT);
let node;
while (node = walker.nextNode()) {
const text = node.nodeValue;
let index = text.indexOf(keyword);
while (index !== -1) {
// Range(範囲)の作成
const range = document.createRange();
range.setStart(node, index);
range.setEnd(node, index + keyword.length);
ranges.push(range);
// 同じテキストノード内に複数存在する場合を考慮
index = text.indexOf(keyword, index + keyword.length);
}
}
return ranges;
}
// 3. 該当するRangeの配列を取得
const myRanges = createRanges(contentElement, searchText);
// 4. Highlightオブジェクトを作成し、Rangeを登録
const searchHighlight = new Highlight(…myRanges);
// 5. CSS.highlightsのレジストリに登録(ここでCSSの ::highlight(search-results) と紐づく)
CSS.highlights.set(‘search-results’, searchHighlight);
どうだ?めちゃくちゃスッキリしているだろ。
DOMを一切破壊していないから、あとから検索ワードが変わったりクリアされたりしても、`CSS.highlights.clear()` や `set()` のし直しだけで、レンダリングコストを最小限に抑えてサクサク動く。
—
3. 現場で使える実務的なTipsと注意点
さて、ここまで聞くと「今すぐプロダクションコードに導入しよう!」と息巻くかもしれないが、シニアの俺からいくつか現場のリアルな注意点を共有しておこう。
① 使えるCSSプロパティの制限に注意しろ
`::highlight` 擬似要素は、何でもかんでもCSSが書けるわけじゃない。ブラウザの描画エンジンがインラインの装飾として処理するため、使えるプロパティには制限がある。
基本的には以下のプロパティが安全に使える。
- `background-color`
- `color`
- `text-decoration` 関連(underline, line-throughなど)
- `text-shadow`
- `box-shadow`(※環境によって挙動が怪しい場合があるため要検証)
逆に、`display` や `margin`、`padding` などのレイアウトに影響を与えるプロパティは効かない(そもそもDOMを変えてないんだから当然だな)。
② ブラウザサポートとプログレッシブ・エンハンスメント
この機能はモダンブラウザ(Chrome, Edge, Safari 18+など)でサポートが進んでいるが、古いブラウザや一部の環境では動かない可能性がある。
したがって、プロダクションで使う際は、以下のように機能検出(Feature Detection)を必ず行うのがプロの作法だ。
if (CSS.highlights) {
// Highlight APIがサポートされている場合の処理
console.log(“Highlight API is supported!”);
} else {
// フォールバック(従来通りタグで囲む処理など)を行う
console.warn(“Highlight API is not supported in this browser.”);
}
動かない環境であってもサイト全体がクラッシュしないように、こうした防衛的プログラミングを忘れないようにしよう。
—
4. まとめ:これからのテキスト操作のスタンダード
`::highlight` 擬似要素と Highlight API は、これまでの「DOMを書き換える」という重い呪縛から私たちフロントエンドエンジニアを解放してくれる強力な武器だ。
ページ内検索、リアルタイムのコードエディタ、文書の校正ツール、AIチャットのキーワード強調など、応用範囲は無限大にある。
「DOMを汚さない」「パフォーマンスが高い」「JSとCSSの役割分担が美しい」。どれをとっても、現代のWeb開発において採用する価値がある技術だ。
さあ、理屈はわかったな?
次のスプリントのタスクに、さっそくこのスマートなハイライト処理を組み込んで、チームのメンバーを「おっ」と言わせてやろうぜ。実装で詰まったら、いつでも俺に聞きに来い。

コメント