代表的な面接トピック

フロントエンド面接:検索機能にCSS Custom Highlight APIをどのように活用しますか?

フロントエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

ドキュメントリーダーで、テキストノードをラップすることなく数百件の一致箇所をハイライト表示する必要があります。CSS Custom Highlight APIをどのように活用し、古いRange、選択状態、パフォーマンス、互換性をどのように処理しますか?

設問とコンテキスト

ドキュメントリーダーは、リッチテキスト内の検索一致箇所、注釈、現在選択されているヒットを表示します。すべての一致箇所をspanでラップすると、テキスト選択、編集、レイアウトが崩れてしまいます。Rangeのライフサイクル、レジストリの更新、カスケード、アクセシブルな状態、フォールバック、パフォーマンスをカバーするCSS Custom Highlight APIソリューションを設計してください。これはテキスト描画とプログレッシブエンハンスメントに関するfrontendの質問です。

面接官が見ているポイント

  1. JavaScriptのRange、Highlightオブジェクト、::highlight()の違いを明確に理解しているか。
  2. テキスト置換や編集後に古いRangeを再構築できるか。
  3. ハイライトがDOMを変更せず、::selectionとカスケード可能であることを理解しているか。
  4. コントラスト、現在の一致箇所、フォーカス、キーボード操作のセマンティクスを設計できるか。
  5. 安全なフォールバックを提供し、そのコストを定量化できるか。

尋ねるべき確認事項

  • 対象ブラウザやWebViewはそのAPIをサポートしていますか?
  • ドキュメントは編集可能ですか、仮想化されていますか、頻繁に再レンダリングされますか?
  • 一致箇所はいくつ存在し、一致箇所が複数のテキストノードをまたぐことはありますか?
  • ダークモード、ハイコントラスト(forced colors)、スクリーンリーダーのサポートは必須ですか?
  • 非対応環境において、DOMにマークをつけるフォールバックは許容されますか?

30秒で答える要約

「各一致箇所をRangeとして表現し、それらのRangeをHighlightにまとめ、::highlight(name)によるスタイリングのためにCSS.highlightsに登録します。テキストが変更された際は、切り離されたノードを保持し続けるのではなく、古い登録を削除し、テキストのバージョンとオフセットから再構築します。検索、注釈、現在の一致箇所のレイヤーを分離し、色だけに頼らず件数やキーボード状態を提示します。サポート状況を検知して安全にマークされたテキストへフォールバックし、一致件数、更新p95、描画フレーム、メモリをベンチマークします。」

詳細解説

ステップ 1: RangeとHighlightの作成

このAPIを使用すると、JavaScriptで任意のテキストRangeを作成し、それらをHighlightに結合して、そのHighlightをHighlightRegistryに登録できます。CSSはラッパー要素を追加することなく、::highlight(name)でRangeのスタイルを設定します。

javascript
const range = new Range();
range.setStart(textNode, start);
range.setEnd(textNode, end);
const hit = new Highlight(range);
CSS.highlights.set("search", hit);

プロダクションコードでは、ノードをまたぐRangeやノードのライフタイムも適切に処理する必要があります。

ステップ 2: テキスト更新の調整

各一致情報とともに、ドキュメントのバージョン、テキストノードのパス、文字オフセットを保持します。再レンダリングや編集によってノードがDOMツリーから切り離され、そのRangeが無効(stale)になることがあります。古いレジストリエントリを削除し、DOM参照のみを保持するのではなく、新しいテキストインデックスから再構築します。

ステップ 3: レイヤーの管理

検索、注釈、現在の一致箇所、スペルチェックの候補には個別の名前を使用します。::selectionや他のユーザーエージェントのハイライトとの意図したカスケードを定義します。1つの現在ヒット用オブジェクトを更新する際に、すべての一致箇所の再構築が強制されないようにします。

ステップ 4: アクセシブルなフィードバックの提供

色を唯一のシグナルにしてはいけません。現在のヒット件数、フォーカス可能なナビゲーション、可視フォーカスインジケーター、ハイコントラスト(forced colors)向けの代替手段を提供します。ハイライト自体がスクリーンリーダーによって読み上げられると想定せず、結果リストでセマンティックなステータスを明示する必要があります。

ステップ 5: パフォーマンスの制御

入力をデバウンスし、レジストリ更新をバッチ処理し、可能な場合は可視ウィンドウ内の処理に限定します。テキスト走査、更新p95、スタイル計算、スクロールフレームレート、メモリを測定します。キー入力ごとのレジストリ変更は避けます。

ステップ 6: プログレッシブフォールバック

機能を検知し、Custom Highlight、マーク付きDOM、またはプレーンな結果リストを選択します。DOMフォールバックでは、テキストのエスケープ、リンクの保持、重複したネストラッパーの回避が必要です。クライアント側のエンハンスメントが行われる前でも、サーバー出力は読める状態を保つ必要があります。

ステップ 7: テストと受け入れ基準

複数ノードにまたがる一致、再レンダリング、編集、ダークモード、強制カラー、コピー、仮想化、RTL、非対応ブラウザをテストします。結果テキスト、キーボード順序、初回ペイント、スクロールFPS、更新p95、メモリを比較し、フォールバック時にも一致箇所が欠落しないようにします。

模範回答

「ドキュメントのDOMを変更しない方針をとります。一致箇所をRangeとして表現し、それらをHighlightにグループ化してCSS.highlightsに名前を登録し、::highlight()でスタイルを適用します。各結果にはテキストバージョンとオフセットを保持させ、再レンダリング時には古い登録を削除して再構築します。検索、注釈、現在のヒットには別々のレイヤーを設け、件数表示、フォーカス、セマンティックステータスにより現在のヒットをアクセシブルにします。

機能検知により、Custom Highlightか安全なマーク付きDOM/結果リストフォールバックかを選択します。デバウンス、バッチ更新、可視ウィンドウ制限により負荷を制御します。受け入れテストでは、ノードをまたぐRange、コピー、編集、ダークモード、強制カラー、仮想化、非対応ブラウザを網羅し、p95、FPS、メモリ、結果の整合性を比較・検証します。」

よくある間違い

  • すべてのヒットをspanで囲む → テキスト構造や選択状態を壊す → Range/Highlightを優先する。
  • 古いRangeを保持し続ける → 再レンダリングでノードが切り離される → テキストバージョンに基づいて再構築する。
  • 色だけに頼る → ハイコントラストモードや色覚多様性のあるユーザーが状態を認識できない → セマンティックなステータスとフォーカスを追加する。
  • キー入力ごとに状態を変更する → スタイル計算の多発(churn)を引き起こす → デバウンスとバッチ処理を行う。
  • フォールバックHTMLを文字列結合で作成する → インジェクションやネストの崩れのリスクが生じる → エスケープ処理を行い、マークする範囲を制限する。

フォローアップ質問と回答

フォローアップ 1: なぜspanを使用してはいけないのですか?

Custom HighlightはDOMを変更しないため、選択、編集、レイアウトへの干渉を減らすことができます。spanはエスケープとネスト制御を備えた互換性用フォールバックとして残します。

フォローアップ 2: 編集後にRangeはどうなりますか?

そのノードが置き換えられたり切り離されたりする可能性があります。古い参照を使い続けるのではなく、新しいテキストバージョンとオフセットから再構築します。

フォローアップ 3: スクリーンリーダーはハイライトを読み上げますか?

読み上げられると想定してはいけません。検索結果の件数、現在の状態、キーボードナビゲーションをセマンティックなテキストとして明示してください。

フォローアップ 4: リグレッションがないことをどのように証明しますか?

固定されたドキュメントと一致件数を用いて、更新p95、スタイル計算コスト、スクロールFPS、メモリ、コピー動作、仮想化処理を比較・測定します。

公開情報ソース

関連する質問