v152 · shipped · dom · text input
OpaqueRange
A new live range type for <textarea> and text <input> elements. Unlike DOM Range, an OpaqueRange operates over a form control's value text rather than DOM nodes — enabling getBoundingClientRect(), the CSS Highlights API, and anchored popovers for inline suggestions, AI autocomplete, and spell-check UIs.
#opaque-range flag.
at a glance
| Shipped in | Chrome 152 (Enabled by default) |
|---|---|
| Origin trial from | Chrome 148 (desktop, Android) |
| Extends | AbstractRange (not Range) |
| Spec | whatwg/dom PR #1404 (under active spec discussion; see also whatwg/html #10614 and the MSEdge OpaqueRange explainer) |
| ChromeStatus | 6297362687066112 — OpaqueRange |
why it exists
Building inline-suggestion UI (AI autocomplete, spell-check, editor annotations) inside a <textarea> or <input> requires knowing the pixel position of a text offset in the control's value — but there is no standard API for that. Today developers work around this by mirroring the control's content into a hidden <div> overlay, measuring the clone, then positioning their UI relative to the clone. This is fragile and breaks whenever the control's font, padding, or scroll position differs from the mirror. OpaqueRange makes the browser do the geometry: create a range over value text offsets, call getBoundingClientRect(), and get the correct on-screen bounds. The range is live — its offsets stay valid as the value is edited.
shape of the API
[Exposed=Window]
interface OpaqueRange : AbstractRange {
DOMRect getBoundingClientRect();
DOMRectList getClientRects();
undefined disconnect();
};
partial interface HTMLTextAreaElement {
OpaqueRange createValueRange(unsigned long start, unsigned long end);
};
The createValueRange(start, end) method creates a live range over characters in the control's value. Offsets are 0-based character indices into the value string. If start > end, or if an offset exceeds the value length, the method throws an IndexSizeError DOMException. getBoundingClientRect() returns the union of client rects (or empty rect if not rendered or disconnected), getClientRects() returns line rects, and disconnect() stops tracking changes.
examples
Creating an OpaqueRange
OpaqueRange objects are created via a new createValueRange() method on HTMLTextAreaElement and HTMLInputElement (text type):
const textarea = document.querySelector('textarea');
// Create a range covering characters 4–12 in the textarea's value
const range = textarea.createValueRange(4, 12);
Getting geometry
// Returns the bounding rect of the text span in the control
const rect = range.getBoundingClientRect();
// Returns individual line-level rects (for multi-line spans)
const rects = range.getClientRects();
console.log('Text starts at:', rect.left, rect.top);
Using with the CSS Highlights API
// Highlight a range of value text (e.g., a misspelled word)
const range = textarea.createValueRange(7, 14);
const highlight = new Highlight(range);
CSS.highlights.set('spelling-error', highlight);
Live range behaviour and disconnect
const range = textarea.createValueRange(3, 8);
// User types at position 0 → range offsets update automatically
textarea.value = 'AB' + textarea.value; // inserts at start
// range now covers the same original text at new positions
// Clean up range tracking when no longer needed
range.disconnect();
Source: blink-dev Intent to Ship — OpaqueRange; MSEdge OpaqueRange explainer.
browser support
| Browser | Support |
|---|---|
| Chrome | Chrome 152+ (enabled by default; origin trial Chrome 148–151) |
| Edge | Edge 152+ (origin trial 148–151) |
| Firefox | Positive signal (mozilla/standards-positions #1289) |
| Safari | Support signal (WebKit/standards-positions #541) |
Security and privacy: OpaqueRange operates strictly on the value text of form controls that script already has access to. It encapsulates internal control DOM by returning null for startContainer and endContainer, preventing script from inspecting internal Shadow DOM structure of native inputs or textareas.
Source: chromestatus.com/feature/6297362687066112.see also
- chromestatus — OpaqueRange (6297362687066112)
- ChromeStatus API record 6297362687066112
- whatwg/dom PR #1404 — Add OpaqueRange
- whatwg/html #10614 — Proposal for OpaqueRange
- MSEdge OpaqueRange explainer
- blink-dev Intent to Ship: OpaqueRange
- blink-dev Intent to Experiment: OpaqueRange
- Mozilla standards position #1289
- WebKit standards position #541
- Chromium tracking issue 421421332
- MDN: CSS Custom Highlight API
- MDN: AbstractRange
- chrome-platform-showcase — live demo