Skip to content
Open
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
bd9b54e
very rough draft
stephanieyzhang Oct 2, 2025
accbe23
undo whitespace
stephanieyzhang Oct 2, 2025
4fbc254
additional hooks
stephanieyzhang Oct 6, 2025
96d51de
keep original formatting
stephanieyzhang Oct 16, 2025
3155a15
cleaning up textarea hooks
stephanieyzhang Oct 16, 2025
90163c4
remove unnecessary text control block
stephanieyzhang Oct 16, 2025
7ed3fb7
update mutation alg
stephanieyzhang Oct 23, 2025
202f881
rename to PlainTextRange + tpac changes pt 1
stephanieyzhang Dec 4, 2025
dfe8b08
rename to PlainTextRange + tpac changes pt 2
stephanieyzhang Dec 4, 2025
2eef616
updated to OpaqueRange + moved update range steps from dom spec
stephanieyzhang Jan 14, 2026
0cf1cbf
Merge branch 'whatwg:main' into stzhang-addfcr
stephanieyzhang Jan 14, 2026
5aa605f
Add getValueRange
stephanieyzhang Jan 14, 2026
6b6108c
specify supported input types
stephanieyzhang Jan 14, 2026
e772b1a
add supports opaque range def + reword of range updates
stephanieyzhang Feb 9, 2026
568cff3
update to use internal containers instead of opaque range string
stephanieyzhang Feb 9, 2026
580e13e
rename getValueRange to createValueRange
stephanieyzhang Feb 12, 2026
7ceff82
Add auto-disconnect for input and textarea
stephanieyzhang Apr 28, 2026
40e23bb
Merge branch 'whatwg:main' into stzhang-addfcr
stephanieyzhang May 12, 2026
ba2ae8d
reorder updates before input
stephanieyzhang May 12, 2026
1917a27
Merge branch 'stzhang-addfcr' of https://github.com/stephanieyzhang/h…
stephanieyzhang May 12, 2026
1c79fdf
Merge branch 'whatwg:main' into stzhang-addfcr
stephanieyzhang May 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 199 additions & 6 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -3251,6 +3251,12 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li><dfn data-x-href="https://dom.spec.whatwg.org/#interface-shadowroot"><code>ShadowRoot</code></dfn> interface</li>
<li><dfn data-x-href="https://dom.spec.whatwg.org/#interface-text"><code>Text</code></dfn> interface</li>
<li><dfn data-x-href="https://dom.spec.whatwg.org/#interface-range"><code>Range</code></dfn> interface</li>
<li><dfn data-x-href="https://dom.spec.whatwg.org/#interface-opaquerange"><code>OpaqueRange</code></dfn> interface, and its
<dfn data-x-href="https://dom.spec.whatwg.org/#opaque-range-start-container">start container</dfn>,
<dfn data-x-href="https://dom.spec.whatwg.org/#opaque-range-start">start offset</dfn>,
<dfn data-x-href="https://dom.spec.whatwg.org/#opaque-range-end-container">end container</dfn>, and
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated
<dfn data-x-href="https://dom.spec.whatwg.org/#opaque-range-end">end offset</dfn></li>
<li>The <dfn data-x-href="https://dom.spec.whatwg.org/#supports-opaque-range">supports opaque ranges</dfn> concept</li>

<li><dfn data-x-href="https://dom.spec.whatwg.org/#concept-node-document">node document</dfn> concept</li>
<li><dfn data-x="concept-document-type" data-x-href="https://dom.spec.whatwg.org/#concept-document-type">document type</dfn> concept</li>
Expand Down Expand Up @@ -48558,6 +48564,7 @@ interface <dfn interface>HTMLInputElement</dfn> : <span>HTMLElement</span> {
undefined <span data-x="dom-textarea/input-setRangeText">setRangeText</span>(DOMString replacement);
undefined <span data-x="dom-textarea/input-setRangeText">setRangeText</span>(DOMString replacement, unsigned long start, unsigned long end, optional <span>SelectionMode</span> selectionMode = "preserve");
undefined <span data-x="dom-textarea/input-setSelectionRange">setSelectionRange</span>(unsigned long start, unsigned long end, optional DOMString direction);
[NewObject] <code>OpaqueRange</code> <span data-x="dom-textarea/input-getValueRange">getValueRange</span>(unsigned long start, unsigned long end);
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated

undefined <span data-x="dom-input-showPicker">showPicker</span>();

Expand Down Expand Up @@ -48740,7 +48747,8 @@ interface <dfn interface>HTMLInputElement</dfn> : <span>HTMLElement</span> {
<code data-x="dom-textarea/input-selectionEnd">selectionEnd</code>, and
<code data-x="dom-textarea/input-selectionDirection">selectionDirection</code>, IDL attributes, the
<code data-x="dom-textarea/input-setRangeText">setRangeText()</code> and
<code data-x="dom-textarea/input-setSelectionRange">setSelectionRange()</code> methods, the
<code data-x="dom-textarea/input-setSelectionRange">setSelectionRange()</code>, and
<code data-x="dom-textarea/input-getValueRange">getValueRange()</code> methods, the
<code data-x="dom-input-stepUp">stepUp()</code> and
<code data-x="dom-input-stepDown">stepDown()</code> methods, and the
<code data-x="event-input">input</code> and
Expand Down Expand Up @@ -49764,6 +49772,30 @@ interface <dfn interface>HTMLInputElement</dfn> : <span>HTMLElement</span> {
<td class="no"> &middot; <!-- Date -->
<!-- <td class="no"> &middot; Month -->
<!-- <td class="no"> &middot; Week -->
<!-- <td class="no"> &middot; Time -->
<td class="no"> &middot; <!-- Local Date and Time -->
<td class="no"> &middot; <!-- Number -->
<td class="no"> &middot; <!-- Range -->
<td class="no"> &middot; <!-- Color -->
<td class="no"> &middot; <!-- Checkbox -->
<!-- <td class="no"> &middot; Radio Button -->
<td class="no"> &middot; <!-- File Upload -->
<td class="no"> &middot; <!-- Submit Button -->
<td class="no"> &middot; <!-- Image Button -->
<td class="no"> &middot; <!-- Reset Button -->
<!-- <td class="no"> &middot; Button -->
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated

<tr>
<th> <code data-x="dom-textarea/input-getValueRange">getValueRange()</code>
<td class="no"> &middot; <!-- Hidden -->
<td class="yes"> Yes <!-- Text -->
<!-- <td class="yes"> Yes Search -->
<td class="yes"> Yes <!-- Telephone, URL -->
<td class="no"> &middot; <!-- Email -->
<td class="yes"> Yes <!-- Password -->
<td class="no"> &middot; <!-- Date -->
<!-- <td class="no"> &middot; Month -->
<!-- <td class="no"> &middot; Week -->
<!-- <td class="no"> &middot; Time -->
<td class="no"> &middot; <!-- Local Date and Time -->
<td class="no"> &middot; <!-- Number -->
Expand Down Expand Up @@ -49919,6 +49951,11 @@ interface <dfn interface>HTMLInputElement</dfn> : <span>HTMLElement</span> {
to the value of the <code data-x="attr-input-value">value</code> content attribute, if there is
one, or the empty string otherwise, and then run the current <span>value sanitization
algorithm</span>, if one is defined.</span></p>

<p>If the element <span>supports opaque ranges</span> and this operation changes its <span
data-x="concept-fe-value">value</span>, then run the <span>opaque range full replacement
steps</span> with the element, the old value's <span>length</span>, and the new value's
<span>length</span>.</p>
</div>

<div w-nodev>
Expand Down Expand Up @@ -49964,7 +50001,10 @@ interface <dfn interface>HTMLInputElement</dfn> : <span>HTMLElement</span> {
attribute and false if it does not, empty the list of <span
data-x="concept-input-type-file-selected">selected files</span>, and then invoke the <span>value
sanitization algorithm</span>, if the <code data-x="attr-input-type">type</code> attribute's
current state defines one.</p>
current state defines one. If the element <span>supports opaque ranges</span> and its <span
data-x="concept-fe-value">value</span> changed, then run the <span>opaque range full replacement
steps</span> with the element, the old value's <span>length</span>, and the new value's
<span>length</span>.</p>
</div>

<p>Each <code>input</code> element can be <i data-x="concept-fe-mutable">mutable</i>. Except where
Expand Down Expand Up @@ -50022,6 +50062,13 @@ interface <dfn interface>HTMLInputElement</dfn> : <span>HTMLElement</span> {
<var>copy</var>.</p>
</div>

<div algorithm>
<p>The <code>input</code> <span>HTML element removing steps</span>, given
<var>removedNode</var> and <var>oldParent</var>, are: if <var>removedNode</var> <span>supports
opaque ranges</span>, then set <var>removedNode</var>'s <span>set of associated
OpaqueRanges</span> to an empty <span>set</span>.</p>
</div>

<div algorithm>
<p>The <span>activation behavior</span> for <code>input</code> elements <var>element</var>, given
<var>event</var>, are these steps:</p>
Expand Down Expand Up @@ -50175,6 +50222,10 @@ interface <dfn interface>HTMLInputElement</dfn> : <span>HTMLElement</span> {
element's <span data-x="concept-textarea/input-cursor">text entry cursor position</span> to the
beginning of the text control, and <span data-x="set the selection direction">set its selection
direction</span> to "<code data-x="">none</code>".</p></li>

<li><p>If <var>previouslySelectable</var> is true and <var>nowSelectable</var> is false, then for
each <code>OpaqueRange</code> in this element's <span>set of associated OpaqueRanges</span>, set
its <span>start offset</span> and <span>end offset</span> to 0.</p></li>
</ol>
</div>

Expand Down Expand Up @@ -54900,6 +54951,12 @@ You cannot submit this form when the field is incorrect.</samp></pre>
<li><p>Invoke the <span>value sanitization algorithm</span>, if the element's <code
data-x="attr-input-type">type</code> attribute's current state defines one.</p></li>

<li><p>If the element's <span data-x="concept-fe-value">value</span> is different
from <var>oldValue</var>, and the element <span>supports opaque ranges</span>, then run
the <span>opaque range full replacement steps</span> with the element,
<var>oldValue</var>'s <span>length</span>, and the current <span
data-x="concept-fe-value">value</span>'s <span>length</span>.</p></li>

<li><p>If the element's <span data-x="concept-fe-value">value</span> (after applying the
<span>value sanitization algorithm</span>) is different from <var>oldValue</var>, and the
element has a <span data-x="concept-textarea/input-cursor">text entry cursor position</span>,
Expand Down Expand Up @@ -55386,6 +55443,9 @@ You cannot submit this form when the field is incorrect.</samp></pre>
data-x="dom-Event-composed">composed</code> attributes initialized to true. The corresponding
<code data-x="event-change">change</code> event, if any, will be fired when the control <a
href="#unfocus-causes-change-event">loses focus</a>.</p>
<p>Before queuing that task, if the element <span>supports opaque ranges</span>, then run the
<span>opaque range replacement steps</span> with the element, the edit's code unit offset, the
number of code units removed, and the number of code units inserted.</p>
</div>

<p class="example">Examples of a user changing the element's <span
Expand Down Expand Up @@ -57618,6 +57678,7 @@ interface <dfn interface>HTMLTextAreaElement</dfn> : <span>HTMLElement</span> {
undefined <span data-x="dom-textarea/input-setRangeText">setRangeText</span>(DOMString replacement);
undefined <span data-x="dom-textarea/input-setRangeText">setRangeText</span>(DOMString replacement, unsigned long start, unsigned long end, optional <span>SelectionMode</span> selectionMode = "preserve");
undefined <span data-x="dom-textarea/input-setSelectionRange">setSelectionRange</span>(unsigned long start, unsigned long end, optional DOMString direction);
[NewObject] <code>OpaqueRange</code> <span data-x="dom-textarea/input-getValueRange">getValueRange</span>(unsigned long start, unsigned long end);
};</code></pre>
</dd>
<dd w-dev>Uses <code>HTMLTextAreaElement</code>.</dd>
Expand Down Expand Up @@ -57688,6 +57749,9 @@ interface <dfn interface>HTMLTextAreaElement</dfn> : <span>HTMLElement</span> {
interaction before queuing the task; for example, a user agent could wait for the user to have not
hit a key for 100ms, so as to only fire the event when the user pauses, instead of continuously
for each keystroke.</p>
<p>Before queuing that task, if the element <span>supports opaque ranges</span>, then run the
<span>opaque range replacement steps</span> with the element, the edit's code unit offset, the
number of code units removed, and the number of code units inserted.</p>
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated
</div>
<!-- same text is present in the <input> section -->

Expand All @@ -57704,19 +57768,33 @@ interface <dfn interface>HTMLTextAreaElement</dfn> : <span>HTMLElement</span> {
value flag</span> from <var>node</var> to <var>copy</var>.</p>
</div>

<div algorithm>
<p>The <code>textarea</code> <span>HTML element removing steps</span>, given
<var>removedNode</var> and <var>oldParent</var>, are: set <var>removedNode</var>'s <span>set of
associated OpaqueRanges</span> to an empty <span>set</span>.</p>
</div>

<div algorithm>
<p>The <span>children changed steps</span> for <code>textarea</code> elements must, if the
element's <span data-x="concept-fe-dirty">dirty value flag</span> is false, set the element's
<span data-x="concept-textarea-raw-value">raw value</span> to its <span>child text
content</span>.</p>
content</span>. If this changes the element's <span
data-x="concept-textarea-raw-value">raw value</span>, and the element <span>supports opaque
ranges</span>, then run the <span>opaque range full replacement steps</span> with the element,
the length of the previous <span data-x="concept-fe-api-value">API value</span>, and the
length of the new <span data-x="concept-fe-api-value">API value</span>.</p>
</div>

<div algorithm>
<p>The <span data-x="concept-form-reset-control">reset algorithm</span> for <code>textarea</code>
elements is to set the <span>user validity</span> to false, the <span
data-x="concept-fe-dirty">dirty value flag</span> back to false, and the <span
data-x="concept-textarea-raw-value">raw value</span> to its <span>child text
content</span>.</p>
content</span>. If this changes the element's <span data-x="concept-fe-api-value">API
value</span>, and the element <span>supports opaque ranges</span>, then run the <span>opaque
range full replacement steps</span> with the element, the length of the previous <span
data-x="concept-fe-api-value">API value</span>, and the length of the new <span
data-x="concept-fe-api-value">API value</span>.</p>
</div>

<div algorithm>
Expand Down Expand Up @@ -57989,8 +58067,13 @@ interface <dfn interface>HTMLTextAreaElement</dfn> : <span>HTMLElement</span> {
<li><p>Set this element's <span data-x="concept-textarea-raw-value">raw value</span> to the new
value.</p></li>

<li><p>Set this element's <span data-x="concept-fe-dirty">dirty value flag</span> to
true.</p></li>
<li><p>Set this element's <span data-x="concept-fe-dirty">dirty value flag</span> to true.</p></li>

<li><p>If this changes the element's <span data-x="concept-fe-api-value">API value</span>,
and the element <span>supports opaque ranges</span>, then run the <span>opaque range full
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated
replacement steps</span> with this element, the <span>length</span> of
Comment thread
stephanieyzhang marked this conversation as resolved.
<var>oldAPIValue</var>, and the <span>length</span> of the element's new <span
data-x="concept-fe-api-value">API value</span>.</p></li>

<li><p>If the new <span data-x="concept-fe-api-value">API value</span> is different from
<var>oldAPIValue</var>, then move the <span data-x="concept-textarea/input-cursor">text entry
Expand Down Expand Up @@ -61926,6 +62009,24 @@ MIT Room 32-G524

</dl>
</dd>

<dt><code data-x=""><var>range</var> = <var>element</var>.<span subdfn data-x="dom-textarea/input-getValueRange">getValueRange</span>(<var>start</var>, <var>end</var>)</code></dt>

<dd>
<p>Returns an <code>OpaqueRange</code> object representing the portion of the element's <span
data-x="concept-textarea/input-relevant-value">relevant value</span> from <var>start</var> to
<var>end</var>.</p>

<p>Throws a <span>"<code>NotSupportedError</code>"</span> <code>DOMException</code> if the
element does not <span data-x="supports opaque ranges">support opaque ranges</span>.</p>

<p>Throws an <span>"<code>IndexSizeError</code>"</span> <code>DOMException</code> if
<var>start</var> or <var>end</var> is greater than the length of the <span
data-x="concept-textarea/input-relevant-value">relevant value</span>.</p>

<p>If <var>start</var> is greater than <var>end</var>, the range is collapsed to
<var>start</var>.</p>
</dd>
</dl>

<div w-nodev>
Expand Down Expand Up @@ -62298,6 +62399,12 @@ MIT Room 32-G524

<li><p>Let <var>new end</var> be the sum of <var>start</var> and <var>new length</var>.</p></li>

<li><p>Let <var>deleted count</var> be max(0, <var>end</var> minus <var>start</var>).</p></li>

<li><p>If this element <span>supports opaque ranges</span>, then run the <span>opaque range
replacement steps</span> with this element, <var>start</var>, <var>deleted count</var>, and
<var>new length</var>.</p></li>

<li>
<p>Run the appropriate set of substeps from the following list:</p>

Expand Down Expand Up @@ -62362,6 +62469,92 @@ MIT Room 32-G524
</ol>
</div>

<p>A <code>textarea</code> element, or an <code>input</code> element whose <code
data-x="attr-input-type">type</code> attribute is in the <span
data-x="attr-input-type-text">Text</span>, <span data-x="attr-input-type-search">Search</span>,
<span data-x="attr-input-type-tel">Telephone</span>, <span
data-x="attr-input-type-url">URL</span>, or <span
data-x="attr-input-type-password">Password</span> state, <span>supports opaque ranges</span>.</p>

<p>Each element that <span>supports opaque ranges</span> has a <dfn>set of associated
Comment thread
stephanieyzhang marked this conversation as resolved.
Comment thread
stephanieyzhang marked this conversation as resolved.
OpaqueRanges</dfn>, which is a <span>set</span> of <code>OpaqueRange</code> objects, initially
empty.</p>

<p>For an element that <span>supports opaque ranges</span>, the <dfn>opaque range internal
container</dfn> is the internal node representing the element's <span
data-x="concept-textarea/input-relevant-value">relevant value</span> text.</p>

<div algorithm>
<p>The <dfn>opaque range full replacement steps</dfn>, given an element
<var>element</var>, an integer <var>oldLength</var>, and an integer <var>newLength</var>, are to
run the <span>opaque range replacement steps</span> with <var>element</var>, 0,
<var>oldLength</var>, and <var>newLength</var>.</p>
</div>

<div algorithm>
<p>The <dfn>opaque range replacement steps</dfn>, given an element <var>element</var>,
a non-negative integer <var>offset</var>, a non-negative integer <var>count</var>, and a
non-negative integer <var>insertedLength</var>, are:</p>

<ol>
<li><p>For each <code>OpaqueRange</code> in <var>element</var>'s <span>set of associated
OpaqueRanges</span> whose <span>start offset</span> is greater than <var>offset</var> but less
than or equal to <var>offset</var> plus <var>count</var>, set its <span>start offset</span> to
<var>offset</var>.</p></li>

<li><p>For each <code>OpaqueRange</code> in <var>element</var>'s <span>set of associated
OpaqueRanges</span> whose <span>end offset</span> is greater than <var>offset</var> but less
than or equal to <var>offset</var> plus <var>count</var>, set its <span>end offset</span> to
<var>offset</var>.</p></li>

<li><p>For each <code>OpaqueRange</code> in <var>element</var>'s <span>set of associated
OpaqueRanges</span> whose <span>start offset</span> is greater than <var>offset</var> plus
<var>count</var>, increase its <span>start offset</span> by <var>insertedLength</var> and
decrease it by <var>count</var>.</p></li>

<li><p>For each <code>OpaqueRange</code> in <var>element</var>'s <span>set of associated
OpaqueRanges</span> whose <span>end offset</span> is greater than <var>offset</var> plus
<var>count</var>, increase its <span>end offset</span> by <var>insertedLength</var> and
decrease it by <var>count</var>.</p></li>
</ol>

<p class="note">These updates operate on UTF-16 code unit indices.</p>
</div>

<div algorithm>
<p>The <dfn method for="HTMLInputElement,HTMLTextAreaElement"><code
data-x="dom-textarea/input-getValueRange">getValueRange(<var>start</var>,
<var>end</var>)</code></dfn> method, when invoked, must run the following steps:</p>

<ol>
<li><p>If this element is an <code>input</code> element, and <code
data-x="dom-textarea/input-getValueRange">getValueRange()</code> <span data-x="do not
apply">does not apply</span> to this element, then throw a
<span>"<code>NotSupportedError</code>"</span> <code>DOMException</code>.</p></li>

<li><p>Let <var>length</var> be the <span>length</span> of this element's <span
data-x="concept-textarea/input-relevant-value">relevant value</span>.</p></li>

<li><p>If <var>start</var> is greater than <var>length</var>, then throw an
<span>"<code>IndexSizeError</code>"</span> <code>DOMException</code>.</p></li>

<li><p>If <var>end</var> is greater than <var>length</var>, then throw an
<span>"<code>IndexSizeError</code>"</span> <code>DOMException</code>.</p></li>

<li><p>If <var>start</var> is greater than <var>end</var>, then set <var>end</var> to
<var>start</var>.</p></li>

<li><p>Let <var>range</var> be a new <code>OpaqueRange</code> with <span>start container</span>
and <span>end container</span> set to this element's <span>opaque range internal container</span>,
<span>start offset</span> <var>start</var>, and <span>end offset</span> <var>end</var>.</p></li>

<li><p><span data-x="set append">Append</span> <var>range</var> to this element's <span>set of
associated OpaqueRanges</span>.</p></li>

<li><p>Return <var>range</var>.</p></li>
</ol>
</div>

<p>The <code data-x="dom-textarea/input-setRangeText">setRangeText()</code> method uses the
following enumeration:</p>

Expand Down