一個遊標座標等了十幾年:OpaqueRange 把輸入框的黑盒打開了一條縫
從 Chrome 152 的 OpaqueRange API 看起,觀察瀏覽器輸入框這個黑盒如何在十幾年的替代方案之後,終於交出了一套被設計過的定位介面。
(事件回顧:Chrome 152 於 2026 年 8 月正式發布 OpaqueRange API;掘金部落格於 2026 年 9 月 28 日撰文梳理其用法,讓這個 API 再度被討論。)
先從一個再日常不過的畫面看起:使用者在搜尋框打字,遊標閃著,下拉選單應該出現在遊標正下方。這個介面我們每天都見,幾乎沒有人覺得它有什麼技術難度。但在 2026 年 8 月之前,網頁開發者做不到這件事,精確地說,他們拿得到遊標在字串裡的索引,卻拿不到那個索引在螢幕上的座標。
這中間的落差,就是 <input> 與 <textarea> 長年以來的設計取捨:瀏覽器把輸入框內部當成不可見的渲染區,selectionStart 給你文字層的資訊,getBoundingClientRect() 只對元素本身有效,內部沒有任何可尋址的 DOM 節點。介面在視覺上是開放的,在結構上是封閉的。
土辦法的材料學
開發者被這個封閉結構逼出幾種替代方案,每一種都是某種材質上的妥協。
第一種是鏡像元素:在輸入框下方藏一個樣式完全相同的 <div>,用 <span> 逐字包裹,再以 span 的矩形回推遊標位置。這做法脆弱得像用紙模型量尺寸,字體、行高、換行、滾動任一項變動,鏡像就對不齊。第二種更激進,直接放棄原生輸入框,用 contenteditable 的 div 造一個假的,換到內部每個字符都是真實節點的代價,是輸入法、撤銷、自動填充與無障礙支援全部重做。第三種是引入 CodeMirror 或 Monaco 這類編輯器庫,本質上仍是第二種的包裝,體積動輒數百 KB。
三種方案的共同點,是為了拿到一組座標,重建了整個輸入介面。這在工程上是繞路,在設計上則是一個訊號:需求存在了十幾年,標準介面卻一直缺席。
這與我們先前觀察的2026 年 HTML 新元素如何重新排版網頁底層介面語言是同一條脈絡:網頁最基礎的介面能力,正在一塊一塊補齊。
OpaqueRange 的介面設計
OpaqueRange 的命名本身就是一份設計聲明。「Opaque(不透明的)」明說了邊界:你不需要、也不能知道輸入框內部如何渲染,你只拿到幾何資訊與高亮能力。它不暴露內部 DOM 結構,這一點保留了瀏覽器對渲染的控制權,同時把開發者真正需要的東西交出來。
用法收斂成三步。在元素上呼叫 createValueRange(0, 5) 取得一段即時的文字範圍;對範圍呼叫 getBoundingClientRect() 得到螢幕座標,跨行文字則用 getClientRects() 取得每行一個的矩形;再透過與 CSS Custom Highlight API 的整合,對這段文字套用自訂高亮,例如拼錯單字下的紅色波浪線。
值得留意的是「即時」這個性質:範圍對應的是輸入框中當下的文字,輸入持續變動時它跟著活著。這讓它比較接近一種狀態的參照,而非一次性的快照,對自動補全這類與遊標共舞的介面來說,正是需要的抽象層級。
從權宜到標準的收斂
把鏡像元素和 OpaqueRange 放在一起比較,差別清楚可見。前者用外部複製去猜內部狀態,後者讓內部主動報告幾何;前者維護的是一整套易碎的對位機制,後者維護的是兩個方法呼叫。對多數只需要「遊標在哪、這段字在哪」的場景,標準化把十幾年的 workaround 收斂成一條公開的介面。
這件事小,但方向明確:瀏覽器沒有選擇把輸入框內部打開成 DOM,而是設計了一層剛好夠用的不透明介面。封閉的渲染權與開放的幾何資訊,在同一個 API 裡各就各位。對一個以黑盒聞名十幾年的元件來說,這大概是最節制、也最恰當的開法。
主題 介面與互動設計
標籤 #opaquerangeapi+介面設計、#textarea+網頁標準、#chrome152+開發者體驗