具代表性的面試主題

前端面試:如何用 CSS Custom Highlight API 實作搜尋高亮?

前端困難
Offer.cc 編輯團隊發佈 更新

題幹

文件閱讀器要同時高亮數百個命中片段,且不能包裹原文節點。你會如何使用 CSS Custom Highlight API,並處理 Range 失效、選取狀態、效能與相容?

題幹與適用場景

線上文件閱讀器要顯示搜尋結果、批註與目前命中項。不能為每個命中插入 span,以免破壞選取、編輯和版面。請設計 CSS Custom Highlight API,說明 Range 管理、註冊表更新、層疊、回退與效能驗收。核心考察文字渲染與漸進增強,歸為 frontend

面試官考察點

  1. 區分 Range、Highlight 與 ::highlight()
  2. 處理重渲染後 Range 失效和增量更新。
  3. 理解高亮不改 DOM 及與 ::selection 的層疊。
  4. 設計顏色、目前命中和鍵盤導覽的無障礙方案。
  5. 提供不支援瀏覽器回退並量化效能。

回答前需要釐清的問題

  • 目標瀏覽器和 WebView 是否支援 API?
  • 文件是否會編輯、虛擬化或頻繁替換節點?
  • 同時最多多少命中,是否跨文字節點?
  • 是否需要暗色、強對比度與讀屏支援?
  • 不支援時可否接受 DOM 標記或只顯示結果列表?

30 秒回答框架

「我把每個命中建成 Range,放入 Highlight,再以 CSS.highlights 註冊名稱,由 ::highlight(name) 統一繪製。文字更新後刪除舊 Range 並按文字版本重建,避免保留脫離 DOM 的節點。顏色遵循對比度,當前命中另設樣式和鍵盤導覽。不支援時回退到安全標記節點,並量測命中數、更新耗時、FPS 和記憶體。」

分步驟深入解答

第一步:建立 Range 與 Highlight

API 讓 JavaScript 建立文字 Range,再把多個 Range 組成 Highlight 並註冊到 HighlightRegistry。CSS 用 ::highlight(name) 設定樣式,不必改動原文 DOM。

第二步:處理跨節點與更新

結果保存文件版本、節點路徑和偏移。重渲染或編輯後舊 Range 可能失效;先移除舊 Highlight,再依新文字索引重建,不能只保存節點引用。

第三步:管理多層級

搜尋、批註、目前命中和拼字提示使用不同名稱,並確認與 ::selection 等 UA 高亮的層疊。不要用一個物件覆蓋所有顏色。

第四步:設計無障礙

顏色不是唯一資訊。提供目前命中計數、可聚焦導覽、焦點環與強對比度替代。高亮不一定被讀屏朗讀,結果列表要同步提供語義。

第五步:控制效能

對輸入 debounce、批次更新註冊表、限制可見窗口,量測掃描時間、更新 p95、FPS 和記憶體。避免每個按鍵逐一 set。

第六步:漸進增強回退

能力檢測後選擇 Custom Highlight、DOM 包裹或純文字結果列表。DOM 回退要轉義文字、避免重複巢狀,服務端先輸出可讀原文。

第七步:測試與驗收

覆蓋跨節點、重渲染、編輯、暗色、強對比度、複製、虛擬列表、RTL 和不支援瀏覽器,比較結果、鍵盤順序、首屏時間、FPS、p95 與記憶體。

高品質示範回答

「我會用 Range 表示命中、Highlight 聚合,再透過 CSS.highlights::highlight() 繪製,保持原文 DOM 不變。結果保存文字版本與偏移,重渲染後刪除舊註冊並重建。搜尋、批註、目前命中分層,當前命中提供計數、焦點和語義狀態。先做能力檢測,不支援時回退安全 DOM 標記或結果列表;用 debounce、批次更新與可見窗口控制成本。」

常見錯誤

  • 每個命中插入 span → 破壞選取與結構 → 優先 Range/Highlight。
  • 保存舊 Range → 重渲染後失效 → 按文字版本重建。
  • 只用顏色 → 強對比度使用者難辨識 → 提供狀態與焦點。
  • 逐鍵更新 → 造成抖動 → debounce 和批次更新。
  • 回退拼接 HTML → 有注入和巢狀風險 → 轉義並限制範圍。

追問及應對

追問一:為何不用 span?

Custom Highlight 不改 DOM,較少干擾選取、編輯與版面;span 可作回退但要轉義。

追問二:編輯後 Range 怎麼辦?

依新文字版本和偏移重建,不能繼續使用舊節點引用。

追問三:讀屏會朗讀高亮嗎?

不能假設,需以語義文字、計數和鍵盤導覽表達狀態。

追問四:如何證明沒有效能回退?

固定文件和命中數,比較更新 p95、FPS、記憶體、複製和虛擬化場景。

公開來源

同類題目