给 input 加 autocomplete 气泡,十年都要靠 div 模拟——今天 OpaqueRange 把这件事彻底变了

做过搜索框联想词、输入法候选词、拼写检查高亮的工程师都知道,最难的事根本不是渲染,是拿到文字在 input 里的位置。

原生 <input><textarea> 的内部结构是浏览器私有实现,开发者拿不到任何文本节点信息。想在「李」字下方弹个气泡,你根本不知道那个字在屏幕坐标的哪里。

十年了,主流解法是放弃原生控件,用 contenteditable 的 div 自己实现一套输入框。Gmail 的搜索框、JetBrains IDE 的搜索、苹果的输入法候选窗——全是用 div 堆出来的。原因无他:原生 input 拿不到文字坐标,气泡没法跟字对齐。

Chrome 152 正式上线的 OpaqueRange API,从根上把这件事变了。

它做的事情很直接:在 <input><textarea> 的值文本上,给你一个「像 Range 但不暴露内部 DOM」的对象。

// 拿到光标或选中范围对应的 OpaqueRange
const input = document.querySelector(input);
const range = input.createValueRange();

if (range) {
  // 像普通 Range 一样拿几何信息
  const rect = range.getBoundingClientRect();
  // rect.top / rect.left 就是文字在屏幕上的位置
  showPopoverAt(rect.left, rect.bottom);
}

getBoundingClientRect() 返回的光标/选中文字坐标,和你用 contenteditable div 模拟出来的精度完全一致。气泡可以直接挂在文字下方,不用任何 hack。

再配合 CSS Custom Highlight API,在原生 input 里做拼写检查高亮:

// OpaqueRange + CSS Custom Highlight API = 原生 input 内的精确高亮
const range = input.createValueRange();
const highlight = new Highlight(range);
cssHighlighting.registerHighlight(spell-error, highlight);

// CSS 里直接用
::highlight(spell-error) {
  background-color: yellow;
  text-decoration: wavy underline red;
}

之前为了做这个效果,要维护一个 contenteditable div、监听所有输入事件、重新实现选区和光标逻辑、无障碍兼容——几千行代码。现在一个 API 加几行 CSS。

为什么说「十年」不是夸张:CSSWG 2016 年就有相关提案(caretPositionFromPoint()),实际体验是精度差、性能差、兼容更差。Microsoft Edge 团队在 2023-2026 年间收集了多个来自 Web 开发者的真实需求——Spell Checking API 的 GitHub issue 里有超过 40 个开发者评论说「这个功能没有我们就只能放弃原生 input」,GitHub Edge Explainers 的 issue 也有十几个开发者说「autocomplete 气泡没法精确跟随光标,体验根本做不上去」。OpaqueRange 的前身 FormControlRange(2023 年立项)到 Chrome 152 正式版,整个过程跨越了三年多。

Chrome 152(2026 年 8 月 25 日)正式上线。Firefox 表态 Positive,Safari 也在跟进支持。这个 API 不是 Chrome 私有,是 WHATWG DOM 标准的一部分。

下一步:如果你的项目里现在还有 contenteditable 实现的搜索框或输入组件,可以评估一下迁移成本——OpaqueRange 能让你换回原生 input,同时保持原有的气泡和高亮能力。建议先从「光标位置气泡」这个小功能开始试,不用一次性迁移整个输入组件。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 15 阅读