FormControlRange Interface
What problem are you trying to solve?
The current Range interface methods do not support retrieving or creating Range objects that represent the value (rather than the element itself) of <textarea> and <input> elements.
What solutions exist today?
Currently, if web authors wish to use range-based operations with <textarea> or <input> elements, they must find workarounds, which often involve cloning these elements and their styles into <div>s, which is both difficult to maintain and may impact the web application's performance.
How would you solve it?
The proposed interface provides a way of creating a FormControlRange—a specialized type of Range object—that represents part or all of the value of <textarea> and <input> elements, enabling range-based operations such as getting bounding rects and setting custom highlights, while limiting access to the standard Range API to enforce encapsulation.
Anything else?
This proposal is only scoped to textarea/input element. For other elements, the regular Range interface can already be used for range-based operations.
Link to the explainer for this proposal: FormControlRange
You may want to take a look at https://open-ui.org/components/richer-text-fields.explainer/ which overlaps a lot with this. It might be worth combining the proposals in some capacity or at least making sure nothing is missed from the relevant sections of the open ui proposal.
It seems a bit weird to me to support these instance methods on non-live ranges. Because this API makes it seem like you can treat these ranges the same as live ranges, but you really can't.
You may want to take a look at https://open-ui.org/components/richer-text-fields.explainer/ which overlaps a lot with this. It might be worth combining the proposals in some capacity or at least making sure nothing is missed from the relevant sections of the open ui proposal.
Thanks @keithamus for pointing us to the Open-UI explainer. I agree that both explainers are aligned in direction, and we’ll make sure the relevant sections from each are appropriately covered. We're open to combining the proposals.
Our next step is to do some exploratory prototyping and circle back with you. We’ll then bring it to WHATWG and Open-UI, and open it up for broader discussion and feedback.
It seems a bit weird to me to support these instance methods on non-live ranges. Because this API makes it seem like you can treat these ranges the same as live ranges, but you really can't.
Thanks @annevk for your feedback. We’ll update the explainer to clarify that the new range type is intended to be live. We’ll also add examples to illustrate how it should behave when the underlying control values change.
Regarding your first comment—could you clarify which instance method you found odd?
Apologies, I didn't realize these are meant to be live.
A colleague brought up the idea that the name might be too limiting if in the future we want to use this class to allow custom elements to expose encapsulated ranges to the outside world as well. (That would require quite a bit more API surface of course at that point to be able to control the ranges.) Perhaps ElementRange could work?
And yeah, introducing LiveRange as abstract class might be worthwhile.
cc @smaug----
Thanks @keithamus and @annevk for the thoughtful feedback.
Given the overlap with InputRange in the Richer Text Fields explainer, we suggest treating FormControlRange as a concrete proposal within that broader effort. We’ll update our explainer to clarify that the range type is live, add examples for value changes, and reference the Richer Text Fields explainer directly to avoid duplication.
We’re also considering a rename (e.g. ElementRange, as @annevk suggested) to better reflect extensibility and avoid confusion with InputRange (since the name may imply it only applies to <input> elements).
Please let us know if this direction sounds good to you, as we’d like to align before bringing this to wider review. Thanks!
We synced with @keithamus offline, and he’s aligned with the current direction. We're proceeding with the name FormControlRange for now to match the work done so far (explainer and initial Chromium implementation), but if there are objections or a strong preference for a broader name (e.g., ElementRange), please let us know and we can update it. Thanks!
At which point would FormControlRange be updated if it is like a live Range? After beforeinput, but before input event? What about key events, or dnd etc?
FormControlRange should update only when an edit is committed to the control’s value: after the beforeinput default action (if not canceled) and before input. We want this to mirror how Range updates on DOM mutations. It does not update on raw keydown/keyup. Paste and drag-and-drop trigger a single update once the final text is inserted. IME updates only when text is committed, not during preview. Programmatic edits update immediately, and if beforeinput is canceled there’s no update.
Discussed in https://github.com/whatwg/html/issues/11648#issuecomment-3293932780, agreed to bring to stage 1.
Thank you for referencing this @annevk had not found this yet @keithamus the explainer looks very promising
I'd rather see extending Range to work with selection across shadow boundaries in general, and make it work with form controls. If we're going with FormControlRange, we need a support for form control custom elements.
I'd rather see extending
Rangeto work with selection across shadow boundaries in general, and make it work with form controls. If we're going withFormControlRange, we need a support for form control custom elements.
@rniwa Thanks for the feedback. I know you and @dandclark discussed this in another thread, so I’ll summarize it here to keep the discussion in one place:
Extending Range to cross shadow boundaries or into form controls would mean changing some long-standing assumptions about how it works, like DOM boundary constraints, mutation behavior, and what methods such as selectNodeContents() should do for elements with internal value models. That would add quite a bit of complexity and ambiguity into a core API that’s been stable for a long time.
FormControlRange keeps things simpler and more contained. It’s a separate, live AbstractRange subclass that works entirely within the value space of <input> and <textarea>. This preserves encapsulation while still enabling Range style geometry and selection APIs, without exposing or touching the control’s internal DOM.
We agree that supporting form-associated custom elements is a good next step for future work. Once the model is solid for native text controls, we can explore letting custom elements support these capabilities while preserving encapsulation. We’ve (very) briefly noted it as a possible follow-up in the explainer.
Links to spec PRs:
- DOM: https://github.com/whatwg/dom/pull/1404
- HTML: https://github.com/whatwg/html/pull/11741
- CSSOM: https://github.com/w3c/csswg-drafts/pull/12904
Very excited to see work towards solving this issue — that’s a major limitation of the current state of the Custom Highlight API!
Reading through the explainer, I wonder if web compat research was done before ruling out the first approach (simply letting setStart() and setEnd() to work). Enabling existing primitives to just work instead of introducing new ones that authors need to learn is usually a more ergonomic solution.
Another (minor) concern with this proposed API is what happens if down the line we have form controls encapsulating multiple text fields?
Possibly too late for considering other ways to solve this problem, but just to throw an idea out there, what if instead of solving the problem in Range we solved it by giving regular Range objects a DOM node to hook onto? E.g. a property that exposed a Node element or a NodeList, containing a (virtual) text node for <input> and <textarea>. This has the benefit of being extensible to more complex structures in the future, though it may be slower when there is no actual text node in the control's shadow DOM and it needs to be fabricated at runtime (though that can be done lazily) and kept in sync. This could also pave the way for other elements that display UI text that does not correspond to any actual DOM element (accessible to the page author).
Based on the discussion at TPAC to ensure this is compatible with custom elements and future needs, I think we should explore a new range whose start and end container are fully internal. So they don't return the host, they just return null.
That way it can be constructed at any level of nesting and just passed to outer layers without fear of exposing encapsulated nodes. This does mean that creating it needs to be on <input>, <textarea>, and custom elements directly. The former two through dedicated API and custom elements through API web developers can add themselves.
So API surface-wise you'd have:
OpaqueRange extends AbstractRange(andAbstractRange'sstartContainerandendContainerneed to be nullable). (An alternative might be that we createAbstractAbstractRangewhich doesn't havestartContainerandendContainer. We're not entirely sure what's better.)new OpaqueRange({ startContainer: encapsulatedNode, ... })(possiblyShadowRange,RemoteRange)<input>.getValueRange(startOffset, endOffset)<textarea>.getValueRange(startOffset, endOffset)
I don't think that this range should expose toString(). That's more a feature of selection, not ranges. If ranges supported that it would be very different from selection as it would be node-tree-based and not layout-based.