Skip to content

Commit

Permalink
Make the focus fixup rule more explicit
Browse files Browse the repository at this point in the history
* Split it into two variants: one which runs synchronously on HTML element removal, and one which runs during "update the rendering". Closes #8225.

* After this split, only the "update the rendering" variant causes the normal focus algorithms to run, and thus blur and change events to fire.

* Delete the confusing "somehow unfocused without another element being explicitly focused" sentence.

Fixes #3847. Fixes #6729.
  • Loading branch information
domenic committed Jan 27, 2023
1 parent 901c8c5 commit e233336
Showing 1 changed file with 41 additions and 32 deletions.
73 changes: 41 additions & 32 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,18 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<var>removedNode</var> and optionally <var>oldParent</var>, are defined as the following:</p>

<ol>
<li id="node-remove-focus-fixup">
<p>If <var>removedNode</var>'s <span>node document</span>'s <span data-x="focused area of the
document">focused area</span> is <var>removedNode</var>, then set <var>removedNode</var>'s
<span>node document</span>'s <span data-x="focused area of the document">focused area</span> to
<var>removedNode</var>'s <span>node document</span>'s <span>viewport</span>.</p>

<p class="note">This does <em>not</em> perform the <span>unfocusing steps</span>,
<span>focusing steps</span>, or <span>focus update steps</span>, and thus no <code
data-x="event-blur">blur</code> or <code data-x="event-change">change</code> events are
fired.</p>
</li>

<li><p>If <var>removedNode</var> is an element whose <span
data-x="concept-element-namespace">namespace</span> is the <span>HTML namespace</span>, and this
standard defines <span data-x="html element removing steps">HTML element removing steps</span>
Expand Down Expand Up @@ -59396,9 +59408,9 @@ interface <dfn interface>HTMLDialogElement</dfn> : <span>HTMLElement</span> {

<p class="note" id="note-dialog-plus-focus-fixup">This will cause the <span>focused area of the
document</span> to become <span>inert</span> (unless that currently focused area is a
<span>shadow-including descendant</span> of <var>subject</var>). In such cases, the <span>focus
fixup rule</span> will kick in and reset the <span>focused area of the document</span> to the
<span>viewport</span> for now. In a couple steps we will attempt to find a better candidate to
<span>shadow-including descendant</span> of <var>subject</var>). In such cases, the
<span>focused area of the document</span> will soon be <a href="#focus-fixup-rule">reset</a> to
the <span>viewport</span>. In a couple steps we will attempt to find a better candidate to
focus.</p>
</li>

Expand Down Expand Up @@ -77126,39 +77138,14 @@ partial interface <span id="NavigatorUserActivation-partial">Navigator</span> {
</li>
</ol>

<p>When the <span>currently focused area of a top-level traversable</span> is somehow unfocused
without another element being explicitly focused in its stead, the user agent must
<span>immediately</span> run the <span>unfocusing steps</span> for that object.</p>

<p class="note">The <span>unfocusing steps</span> do not always result in the focus changing,
even when applied to the <span>currently focused area of a top-level traversable</span>. For
<p class="note">The <span>unfocusing steps</span> do not always result in the focus changing, even
when applied to the <span>currently focused area of a top-level traversable</span>. For
example, if the <span>currently focused area of a top-level traversable</span> is a
<span>viewport</span>, then it will usually keep its focus regardless until another
<span>focusable area</span> is explicitly focused with the <span>focusing steps</span>.</p>

<hr>

<p><dfn>Focus fixup rule</dfn>: When the designated <span data-x="focused area of the
document">focused area of the document</span> is removed from that <code>Document</code> in some
way (e.g. it stops being a <span>focusable area</span>, it is removed from the DOM, it becomes
<span>inert</span>, etc.), designate the <code>Document</code>'s <span>viewport</span> to be the
new <span>focused area of the document</span>.</p>

<p class="example">For example, this might happen because an element is removed from its
<code>Document</code>, or has a <code data-x="attr-hidden">hidden</code> attribute added. It might
also happen to an <code>input</code> element when the element gets <span
data-x="concept-fe-disabled">disabled</span>.</p>

<p class="example">In a <code>Document</code> whose <span data-x="focused area of the
document">focused area</span> is a <code>button</code> element, removing, disabling, or hiding
that button would cause the page's new <span data-x="focused area of the document">focused
area</span> to be the <span>viewport</span> of the <code>Document</code>. This would, in turn,
be reflected through the <code
data-x="dom-documentorshadowroot-activeElement">activeElement</code> API as <span>the body
element</span>.</p>

<hr>

<p>The <dfn>focus update steps</dfn>, given an <var>old chain</var>, a <var>new chain</var>, and a <var>new focus target</var> respectively, are as
follows:</p>

Expand Down Expand Up @@ -77205,8 +77192,9 @@ partial interface <span id="NavigatorUserActivation-partial">Navigator</span> {
named <code data-x="event-blur">blur</code> at <var>blur event target</var>, with
<var>related blur target</var> as the related target.</p>

<p class="note">In some cases, e.g. if <var>entry</var> is an <code>area</code>
element's shape, a scrollable region, or a <span>viewport</span>, no event is fired.</p>
<p class="note" id="note-sometimes-no-blur-event">In some cases, e.g., if <var>entry</var> is
an <code>area</code> element's shape, a scrollable region, or a <span>viewport</span>, no
event is fired.</p>
</li>
</ol>
</li>
Expand Down Expand Up @@ -99654,6 +99642,27 @@ import "https://example.com/foo/../module2.mjs";</code></pre>
</ol>
</li>

<li id="focus-fixup-rule">
<p>For each <span>fully active</span> <code>Document</code> in <var>docs</var>, if the <span
data-x="focused area of the document">focused area</span> of that <code>Document</code> is
not a <span>focusable area</span>, then run the <span>focusing steps</span> for that
<code>Document</code>'s <span>viewport</span>.</p>

<p class="example">For example, this might happen because an element has the <code
data-x="attr-hidden">hidden</code> attribute added, causing it to stop <span>being
rendered</span>. It might also happen to an <code>input</code> element when the element gets
<span data-x="concept-fe-disabled">disabled</span>.</p>

<p class="note">This will <a href="#note-sometimes-no-blur-event">usually</a> fire <code
data-x="event-blur">blur</code> events, and possibly <code data-x="event-change">change</code>
events.</p>

<p class="note">In addition to this asynchronous fixup, if the <span>focused area of the
document</span> is removed, there is a <a href="#node-remove-focus-fixup">synchronous
fixup</a>. That one will <em>not</em> fire <code data-x="event-blur">blur</code> or <code
data-x="event-change">change</code> events.</p>
</li>

<li><p>For each <span>fully active</span> <code>Document</code> in <var>docs</var>, <span>run
the update intersection observations steps</span> for that <code>Document</code>, passing in
<var>now</var> as the timestamp. <ref spec=INTERSECTIONOBSERVER></p></li>
Expand Down

0 comments on commit e233336

Please sign in to comment.