You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Update FormControlRange explainer to increase usability (#1131)
This PR updates the `FormControlRange` explainer to clarify encapsulation and improve usability:
- Clarifies `FormControlRange` as an `AbstractRange` over form-control value space with live behavior
- Makes previously hidden methods and properties from `FormControlRange `to be accessible for devs:
- `startContainer`, `endContainer`: returns the host `<input>/<textarea>`
- `startOffset`, `endOffset`: indexes into `element.value` (same units as `selectionStart/selectionEnd`), updated as the text changes.
- Replaces the earlier idea of hiding endpoints or returning` null`/`-1`
Minor wording cleanups were also made for clarity.
Copy file name to clipboardExpand all lines: FormControlRange/explainer.md
+37-33Lines changed: 37 additions & 33 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,9 +15,9 @@
15
15
16
16
## Introduction
17
17
18
-
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. As a result, if web developers want to use the `getBoundingClientRect()` method in a `<textarea>` or `<input>` element to position a popup beneath the user's current caret for delivering contextual autocomplete suggestions or marking syntax errors as users type using the [Custom Highlight API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API), they must find workarounds. These workarounds 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.
18
+
The current `Range` interface methods do not support retrieving or creating ranges that represent the `value` (rather than the element itself) of `<textarea>` and `<input>` elements. As a result, if web developers want to use the `getBoundingClientRect()` method in a `<textarea>` or `<input>` element to position a popup beneath the user's current caret for delivering contextual autocomplete suggestions or marking syntax errors as users type using the [Custom Highlight API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API), they must find workarounds. These workarounds 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.
19
19
20
-
This proposal aims to address these issues by introducing `FormControlRange`, a new type of `Range` object that extends `AbstractRange` and serves as a way to reference spans of text within form control elements.
20
+
This proposal aims to address these issues by introducing `FormControlRange`, a new `AbstractRange` subclass that serves as a way to reference spans of text within form control elements while preserving encapsulation.
21
21
22
22
## User-Facing Problem
23
23
@@ -29,7 +29,7 @@ This proposal aims to address these issues by introducing `FormControlRange`, a
Provide a way of retrieving 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.
227
+
Provide a way to obtain a `FormControlRange`—a specialized, live range for `<textarea>` and `<input>`values implemented as an `AbstractRange` subclass—that enables range-based operations (e.g. getting bounding rects, setting custom highlights, etc.) while restricting standard `Range` mutations to preserve encapsulation of these form controls.
228
228
229
229
### Non-goals
230
230
@@ -234,21 +234,36 @@ Provide a way of retrieving a `FormControlRange`—a specialized type of `Range`
234
234
235
235
## Proposed Approach
236
236
237
-
The `FormControlRange` interface extends `AbstractRange` and provides a controlled way to create limited `Range` objects for the entirety or a part of the `value` of `<textarea>` and `<input>` elements. To protect the inner workings of these elements, `FormControlRange` instances have several limitations on the methods and attributes (from the Range API) that can be accessed through JavaScript.
237
+
The `FormControlRange` interface extends `AbstractRange` and provides a controlled way to reference parts of the `value` of `<textarea>` and `<input>` elements. It exposes useful endpoint properties while limiting certain mutation methods to preserve encapsulation.
238
238
239
239
Unlike `StaticRange`, `FormControlRange` is **live** — it tracks changes to the underlying text in the `<textarea>` or `<input>` element and automatically updates its start and end positions, similar to how a regular `Range` tracks DOM mutations. This ensures that operations like `getBoundingClientRect()` or `toString()` always reflect the element’s current content, even after edits.
240
240
241
241
This live-update behavior also aligns conceptually with the `InputRange()` from [Keith Cirkel’s Richer Text Fields proposal](https://open-ui.org/components/richer-text-fields.explainer/), which takes a broader approach to enabling richer interactions in form controls. While that proposal covers more editing capabilities, `FormControlRange` focuses on a limited, encapsulated `AbstractRange`-based API, but both aim to support dynamic interaction with text as it changes.
242
242
243
-
**Initially available methods and properties:**
243
+
### Properties and Methods
244
+
245
+
#### Properties
246
+
FormControlRange exposes useful endpoint information while maintaining encapsulation:
247
+
-`startContainer` and `endContainer`: Return the host form control element (`<input>` or `<textarea>`). No internal/shadow nodes are exposed.
248
+
-`startOffset` and `endOffset`: Return indices into the form control's `value`. These values match those passed to `setFormControlRange()` and are automatically updated as text changes.
The `setFormControlRange()` method accepts three parameters:
278
-
-`element`: The `<textarea>` or `<input>` element
279
-
-`startOffset`: The start position within the element's value
280
-
-`endOffset`: The end position within the element's value
281
-
282
-
If either `startOffset` or `endOffset` are out of bounds, it will throw a `RangeError` exception. If the `value` of the `<textarea>` or `<input>` element is empty, the `FormControlRange` will represent an empty range.
283
-
284
292
The following sample code showcases how the new `FormControlRange` interface would solve the main use cases laid out in the [User-Facing Problem](#user-facing-problem) section.
285
293
286
294
```html
@@ -341,7 +349,7 @@ As we want the `FormControlRange` interface to be aligned with the current selec
341
349
- URL
342
350
- Password
343
351
344
-
Following this same alignment with selection APIs, the parameters `startOffset` and `endOffset`of the `setFormControlRange()` method will be based on caret position, rather than character index, just as [`selectionStart`](https://html.spec.whatwg.org/#the-textarea-element:dom-textarea/input-selectionstart) and [`selectionEnd`](https://html.spec.whatwg.org/#the-textarea-element:dom-textarea/input-selectionend) are.
352
+
Following this same alignment with selection APIs, `startOffset` and `endOffset`are indices into `element.value`, matching the units used by [`selectionStart`](https://html.spec.whatwg.org/#the-textarea-element:dom-textarea/input-selectionstart) and [`selectionEnd`](https://html.spec.whatwg.org/#the-textarea-element:dom-textarea/input-selectionend).
// Reapply the same highlight object after each edit.
434
-
// The live range automatically adjusts as text changes — no offset recalculation needed.
435
-
textarea.addEventListener("input", () => {
436
-
CSS.highlights.set("tracked-word", highlight);
437
-
});
441
+
// No input-handler needed: as the control’s value changes, the range stays in sync and the highlight repaints automatically.
438
442
```
439
443
If text is inserted before `"hello"`, the highlight automatically shifts so it still covers the same word.
440
444
@@ -518,20 +522,20 @@ This `setFormControlRange()` method would set a new flag `IsFormControl()` in th
518
522
519
523
```javascript
520
524
range.startContainer// Returns startNode
521
-
formRange.startContainer//Flag is on: Throws exception
525
+
formRange.startContainer//Returns the host <textarea>/<input> element
522
526
```
523
527
524
-
This approach addresses encapsulation concerns for ranges in form controls. It also offers better compatibility than the `FormControlRange` interface, since it would work seamlessly with APIs that involve `Range` (not only `AbstractRange`) objects, such as the Selection API:
528
+
This approach addresses encapsulation concerns for ranges in form controls. It also offers better compatibility than the `FormControlRange` interface, since it remains a `Range` and is compatible at the type level with APIs that involve `Range` (not only `AbstractRange`) objects, such as the Selection API. However, Selection behavior for value-space ranges is not defined today and would require additional spec work.
525
529
526
530
```javascript
527
531
// Using this approach
528
-
selection.addRange(formRange) //Works as intended.
532
+
selection.addRange(formRange) //Type-checks; actual behavior would need explicit spec rules
529
533
530
534
// Using FormControlRange interface
531
535
selection.addRange(formRange2) // Throws exception: "parameter 1 is not of type Range."
532
536
```
533
537
534
-
Ultimately, the `FormControlRange` interface was chosen instead. While the `setFormControlRange()` approach offered better API compatibility, it did not provide an explicit distinction between regular and form control ranges—both would be of type `Range`, potentially confusing web authors about why some methods may or may not be available depending on how the `Range` was set (using `setStart()` and `setEnd()` or `setFormControlRange()`).
538
+
Ultimately, the `FormControlRange` interface was chosen instead. While the `setFormControlRange()` approach offered better type-level compatibility, it did not provide an explicit distinction between regular and form control ranges—both would be of type `Range`, potentially confusing web authors about why some methods may or may not be available depending on how the `Range` was set (using `setStart()` and `setEnd()` or `setFormControlRange()`).
0 commit comments