Skip to content
Open
Changes from 9 commits
Commits
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
138 changes: 138 additions & 0 deletions dom.bs
Original file line number Diff line number Diff line change
Expand Up @@ -9892,6 +9892,144 @@ and {{Range/getBoundingClientRect()}} methods are defined in other specification
[[CSSOM-VIEW]]


<h3 id=interface-plaintext-range>Interface {{PlainTextRange}}</h3>

<pre class=idl>
[Exposed=Window]
interface PlainTextRange : AbstractRange {
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated
DOMRectList getClientRects();
DOMRect getBoundingClientRect();
stringifier;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why OpaqueRange offsets are readonly? Or there isn't any method to update them?

Copy link
Copy Markdown
Author

@stephanieyzhang stephanieyzhang Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The offsets are readonly since they're inherited from AbstractRange. Currently the HTML PR defines createValueRange(start, end) on the element for creation. We haven't yet added an API for updating, so that's an open question. Some options could be an element-side API (e.g. updateValueRange(range, start, end)) to keep OpaqueRange generic, or setters directly on OpaqueRange.

Would appreciate your thoughts @annevk @smaug----

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we actually don't want to expose the offsets as per my other comment. I'm not sure if we want to expose collapsed. Perhaps it should just be separate from AbstractRange.

Comment thread
stephanieyzhang marked this conversation as resolved.
</pre>

<p>Objects implementing the {{PlainTextRange}} interface are known as {{PlainTextRange}} objects.</p>

<dl class=domintro>
<dt><code><var ignore>rects</var> = <var ignore>range</var> . {{PlainTextRange/getClientRects()}}</code>
<dd>Returns a {{DOMRectList}} of client rectangles that enclose the selected portion of the host's
<a href="#plaintext-range-string">plain text range string</a>. If the range has no
<a for=PlainTextRange>host</a>, is {{AbstractRange/collapsed}} with no visible caret, or the host
has computed <code>display</code> of <code>none</code> or is not <a>connected</a>, returns an
empty list.

<dt><code><var ignore>rect</var> = <var ignore>range</var> . {{PlainTextRange/getBoundingClientRect()}}</code>
<dd>Returns a {{DOMRect}} that is the union of the rectangles from
{{PlainTextRange/getClientRects()}}. For a {{AbstractRange/collapsed}} range in a visible host,
returns a rectangle of zero width whose height equals the line height at the caret position. If the
host has computed <code>display</code> of <code>none</code> or is not <a>connected</a>, returns a
rectangle with zero width and height.
</dl>

<p>A {{PlainTextRange}} is a range whose <a>boundary points</a> are defined as offsets in a
host-defined <a href="#plaintext-range-string">plain text range string</a> rather than in the
<a>node tree</a>. It has no associated container node; its {{AbstractRange/startContainer}} and
{{AbstractRange/endContainer}} getters return null, and its {{AbstractRange/startOffset}} and
{{AbstractRange/endOffset}} getters return indices into that string.</p>

<p>A {{PlainTextRange}} has associated state:</p>

<ul>
<li><p><dfn export for=PlainTextRange id=plaintext-range-host>host</dfn> (null or an
{{Element}}).</p></li>
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated
<li><p><dfn export for=PlainTextRange id=plaintext-range-start>start offset</dfn> (a non-negative
integer).</p></li>
<li><p><dfn export for=PlainTextRange id=plaintext-range-end>end offset</dfn> (a non-negative
integer).</p></li>
</ul>

<p>The {{AbstractRange/startContainer}} getter steps for {{PlainTextRange}} objects are to return
null.</p>
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated

<p>The {{AbstractRange/endContainer}} getter steps for {{PlainTextRange}} objects are to return
null.</p>

<p>The {{AbstractRange/startOffset}} getter steps for {{PlainTextRange}} objects are to return
<a>this</a>'s <a for=PlainTextRange>start offset</a>.</p>

<p>The {{AbstractRange/endOffset}} getter steps for {{PlainTextRange}} objects are to return
<a>this</a>'s <a for=PlainTextRange>end offset</a>.</p>

<p>The {{AbstractRange/collapsed}} getter steps for {{PlainTextRange}} objects are to return true
if <a>this</a>'s <a for=PlainTextRange>start offset</a> equals <a for=PlainTextRange>end
offset</a>; otherwise false.</p>

<p>An {{Element}} <var>el</var> <dfn export id=supports-plaintext-range>supports plain text
ranges</dfn> if its specification defines that it does.</p>

<p class=note>The HTML Standard defines that certain {{HTMLInputElement}} types and
{{HTMLTextAreaElement}} support plain text ranges over their <code>value</code>. Other
specifications may define additional elements, including custom elements, that support plain text
ranges.</p>

<p>For an element <var>el</var> that <a lt="supports plain text ranges">supports plain text
ranges</a>, its <dfn export id=plaintext-range-string>plain text range string</dfn> is a
specification-defined string that represents the linear editable text for that element. In HTML,
this is typically the string exposed by the element's IDL <code>value</code> attribute. Offsets for
{{PlainTextRange}} are indices into this <a href="#plaintext-range-string">plain text range
string</a> in the inclusive range [0, <code>string.length</code>].</p>

<p>When a {{PlainTextRange}} is created, its <a for=PlainTextRange>host</a> must be set to null and
its <a for=PlainTextRange>start offset</a> and <a for=PlainTextRange>end offset</a> must be set to
0.</p>

<p>To <dfn export id=concept-plaintext-range-set>set a plain text range</dfn> for a
{{PlainTextRange}} <var>range</var> over an {{Element}} <var>host</var> from
<var>startOffset</var> to <var>endOffset</var>, run these steps:</p>

<ol>
<li><p>If <var>host</var> does not <a>support plain text ranges</a>, then <a>throw</a> a
"{{NotSupportedError!!exception}}" {{DOMException}}.</p></li>
<li><p>Let <var>string</var> be <var>host</var>'s <a href="#plaintext-range-string">plain text
range string</a>. Let <var>len</var> be <var>string</var>'s <a for=string>length</a>.</p></li>
<li><p>If <var>startOffset</var> &gt; <var>len</var> or <var>endOffset</var> &gt; <var>len</var>,
then <a>throw</a> an "{{IndexSizeError!!exception}}" {{DOMException}}.</p></li>
<li><p>If <var>startOffset</var> &gt; <var>endOffset</var>, then set <var>endOffset</var> to
<var>startOffset</var>.</p></li>
<li><p>Set <var>range</var>'s <a for=PlainTextRange>host</a> to <var>host</var>, its
<a for=PlainTextRange>start offset</a> to <var>startOffset</var>, and its
<a for=PlainTextRange>end offset</a> to <var>endOffset</var>.</p></li>
</ol>

<p>A {{PlainTextRange}} is live: when its host's <a href="#plaintext-range-string">plain text range
string</a> changes, the range's <a for=PlainTextRange>start offset</a> and <a
for=PlainTextRange>end offset</a> are updated automatically to preserve the same logical content.
These behaviors mirror {{Range}} boundary adjustments in the DOM but are applied to the UTF-16 code
units of the host's string.</p>

<ul>
Comment thread
stephanieyzhang marked this conversation as resolved.
<li><p>Edits before the range: Offsets shift by the net length change.</p></li>
<li><p>Edits after the range: Offsets remain unchanged.</p></li>
<li><p>Edits overlapping the range: If a boundary falls inside text that was removed, move it to
the start of the change. If the edit also inserted new text, remap the boundary into the inserted
span at the closest corresponding offset, not exceeding its end.</p></li>
<li><p>Insertion at the start boundary: A non-{{AbstractRange/collapsed}} range expands to include
the new text. A collapsed range moves after the insertion.</p></li>
<li><p>Insertion at the end boundary: A non-collapsed range does not expand to include the new
text. A collapsed range moves after the insertion.</p></li>
<li><p>Clamping and collapse: Offsets are clamped to the current string length. If the
{{AbstractRange/startOffset}} would exceed the {{AbstractRange/endOffset}}, set the end to the
start.</p></li>
</ul>

<p class=note>Specifications that define elements which <a>support plain text ranges</a> (such as
the HTML Standard for text controls) must normatively define when a {{PlainTextRange}} is created,
how it is associated with a host, when its host becomes null, and when its boundary points are
updated using the rules above.</p>

<p>The <dfn export for=PlainTextRange id=dom-plaintext-range-stringifier>stringification
behavior</dfn> must run these steps:</p>
Comment thread
stephanieyzhang marked this conversation as resolved.
Outdated

<ol>
<li><p>If <a>this</a>'s <a for=PlainTextRange>host</a> is null, then return the empty
string.</p></li>
<li><p>Let <var>string</var> be <a>this</a>'s <a for=PlainTextRange>host</a>'s
<a href="#plaintext-range-string">plain text range string</a>.</p></li>
<li><p>Let <var>start</var> be <a>this</a>'s <a for=PlainTextRange>start offset</a>, and let
<var>end</var> be <a for=PlainTextRange>end offset</a>.</p></li>
<li><p>Return the substring of <var>string</var> from <var>start</var> to <var>end</var>.</p></li>
</ol>


<h2 id="traversal">Traversal</h2>

Expand Down
Loading