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
Copy file name to clipboardExpand all lines: README.md
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -567,7 +567,7 @@ Like [accessibility technology announcements](#accessibility-technology-announce
567
567
568
568
App history navigation interception again gives us the tool to fix this.
569
569
570
-
By default, any navigation that is intercepted and converted into a single-page navigation using `navigateEvent.transitionWhile()` will cause focus to reset to the `<body>` element, or to the first element with the `autofocus=""` attribute set (if there is one). This focus reset will happen in the first animation frame after the promise passed to `transitionWhile()` settles. However, this focus reset will not take place if the user has manually changed focus while the promise was settling, and the element the user changed focus to is still visible and focusable.
570
+
By default, any navigation that is intercepted and converted into a single-page navigation using `navigateEvent.transitionWhile()` will cause focus to reset to the `<body>` element, or to the first element with the `autofocus=""` attribute set (if there is one). This focus reset will happen after the promise passed to `transitionWhile()` settles. However, this focus reset will not take place if the user or developer has manually changed focus while the promise was settling, and that element is still visible and focusable.
571
571
572
572
This behavior can be customized using the second options argument to `transitionWhile()`:
<td>Until all promises passed to {{AppHistoryNavigateEvent/transitionWhile()}} have settled
535
535
<td>So that if the navigation is canceled, we can [=AbortSignal/signal abort=].
536
+
<tr>
537
+
<td>Whether a new element was <a spec="HTML" lt="focusing steps">focused</a>
538
+
<td>Until all promises passed to {{AppHistoryNavigateEvent/transitionWhile()}} have settled
539
+
<td>So that if one was, focus is not reset TODO link
536
540
<tr>
537
541
<td>The {{AppHistoryEntry}} being navigated to
538
542
<td>From when it is determined, until all promises passed to {{AppHistoryNavigateEvent/transitionWhile()}} have settled
@@ -602,6 +606,8 @@ Each {{AppHistory}} object has an associated <dfn for="AppHistory">ongoing navig
602
606
603
607
Each {{AppHistory}} object has an associated <dfn for="AppHistory">ongoing navigation signal</dfn>, which is an {{AbortSignal}} or null, initially null.
604
608
609
+
Each {{AppHistory}} object has an associated <dfn for="AppHistory">focus changed during ongoing navigation</dfn>, which is a boolean, initially false.
610
+
605
611
Each {{AppHistory}} object has an associated <dfn for="AppHistory">ongoing navigation</dfn>, which is an [=app history API navigation=] or null, initially null.
606
612
607
613
Each {{AppHistory}} object has an associated <dfn for="AppHistory">upcoming non-traverse navigation</dfn>, which is an [=app history API navigation=] or null, initially null.
@@ -619,7 +625,7 @@ An <dfn>app history API navigation</dfn> is a [=struct=] with the following [=st
619
625
* A <dfn for="app history API navigation">finished promise</dfn>, a {{Promise}}
620
626
* A <dfn for="app history API navigation">did finish before commit</dfn>, a boolean
621
627
622
-
<p class="note">We need to store the [=AppHistory/ongoing navigation signal=] separately from the [=app history API navigation=] struct, since it needs to be tracked even for navigations that are not via the app history APIs.
628
+
<p class="note">We need to store the [=AppHistory/ongoing navigation signal=]and [=AppHistory/focus changed during ongoing navigation=]separately from the [=app history API navigation=] struct, since they need to be tracked even for navigations that are not via the app history APIs.
623
629
624
630
<div algorithm>
625
631
To <dfn for="AppHistory">set the upcoming non-traverse navigation</dfn> given an {{AppHistory}} |appHistory|, a JavaScript value |info|, and a [=serialized state=]-or-null |serializedState|:
The <dfn attribute for="AppHistoryNavigateEvent">navigationType</dfn>, <dfn attribute for="AppHistoryNavigateEvent">destination</dfn>, <dfn attribute for="AppHistoryNavigateEvent">canTransition</dfn>, <dfn attribute for="AppHistoryNavigateEvent">userInitiated</dfn>, <dfn attribute for="AppHistoryNavigateEvent">hashChange</dfn>, <dfn attribute for="AppHistoryNavigateEvent">signal</dfn>, <dfn attribute for="AppHistoryNavigateEvent">formData</dfn>, and <dfn attribute for="AppHistoryNavigateEvent">info</dfn> getter steps are to return the value that the corresponding attribute was initialized to.
1116
1122
1117
-
An {{AppHistoryNavigateEvent}} has the following associated values which are only conditionally used:
1123
+
An {{AppHistoryNavigateEvent}} has a <dfn for="AppHistoryNavigateEvent">classic history API serialized data</dfn>, a [=serialized state=]-or-null. It is only used in some cases where the event's {{AppHistoryNavigateEvent/navigationType}} is "{{AppHistoryNavigationType/reload}}", "{{AppHistoryNavigationType/push}}" or "{{AppHistoryNavigationType/replace}}", and is set appropriately when the event is [[#navigate-event-firing|fired]].
1118
1124
1119
-
* <dfn for="AppHistoryNavigateEvent">classic history API serialized data</dfn>, a [=serialized state=]-or-null, used when its {{AppHistoryNavigateEvent/navigationType}} is "{{AppHistoryNavigationType/reload}}", "{{AppHistoryNavigationType/push}}" or "{{AppHistoryNavigationType/replace}}"
1125
+
An {{AppHistoryNavigateEvent}} has a <dfn for="AppHistoryNavigateEvent">focus reset behavior</dfn>, an {{AppHistoryFocusReset}}-or-null, initially null.
1120
1126
1121
-
This is set appropriately when the event is [[#navigate-event-firing|fired]].
1122
-
1123
-
An {{AppHistoryNavigateEvent}} also has an associated <dfn for="AppHistoryNavigateEvent">navigation action promises list</dfn>, which is a [=list=] of {{Promise}} objects, initially empty.
1127
+
An {{AppHistoryNavigateEvent}} has an associated <dfn for="AppHistoryNavigateEvent">navigation action promises list</dfn>, which is a [=list=] of {{Promise}} objects, initially empty.
1124
1128
1125
1129
<div algorithm>
1126
1130
The <dfn method for="AppHistoryNavigateEvent">transitionWhile(|newNavigationAction|, |options|)</dfn> method steps are:
@@ -1131,6 +1135,8 @@ An {{AppHistoryNavigateEvent}} also has an associated <dfn for="AppHistoryNaviga
1131
1135
1. If [=this=]'s [=Event/dispatch flag=] is unset, then throw an "{{InvalidStateError}}" {{DOMException}}.
1132
1136
1. If [=this=]'s [=Event/canceled flag=] is set, then throw an "{{InvalidStateError}}" {{DOMException}}.
1133
1137
1. [=list/Append=] |newNavigationAction| to [=this=]'s [=AppHistoryNavigateEvent/navigation action promises list=].
1138
+
1. If [=this=]'s [=AppHistoryNavigateEvent/focus reset behavior=] is not null, and it is not equal to |options|["{{AppHistoryTransitionWhileOptions/focusReset}}"], then the user agent may [=report a warning to the console=] indicating that the {{AppHistoryTransitionWhileOptions/focusReset}} option for a previous call to {{AppHistoryNavigateEvent/transitionWhile()}} was overridden by this new value, and the previous value will be ignored.
1139
+
1. Set [=this=]'s [=AppHistoryNavigateEvent/focus reset behavior=] to |options|["{{AppHistoryTransitionWhileOptions/focusReset}}"].
@@ -1284,6 +1290,7 @@ The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps a
1284
1290
1. Set |appHistory|'s [=AppHistory/ongoing navigate event=] to |event|.
1285
1291
1. [=Assert=]: |appHistory|'s [=AppHistory/ongoing navigation signal=] is null.
1286
1292
1. Set |appHistory|'s [=AppHistory/ongoing navigation signal=] to |event|'s {{AppHistoryNavigateEvent/signal}}.
1293
+
1. Set |appHistory|'s [=AppHistory/focus changed during ongoing navigation=] to false.
1287
1294
1. Let |dispatchResult| be the result of [=dispatching=] |event| at |appHistory|.
1288
1295
1. Set |appHistory|'s [=AppHistory/ongoing navigate event=] to null.
1289
1296
1. If |dispatchResult| is false:
@@ -1305,12 +1312,14 @@ The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps a
1305
1312
1. [=Resolve=] |transition|'s [=AppHistoryTransition/finished promise=] with undefined.
1306
1313
1. If |appHistory|'s [=AppHistory/transition=] is |transition|, then set |appHistory|'s [=AppHistory/transition=] to null.
1307
1314
1. If |ongoingNavigation| is non-null, then [=app history API navigation/resolve the finished promise=] for |ongoingNavigation|.
1315
+
1. [=Potentially reset the focus=] given |appHistory| and |event|.
1308
1316
and the following failure steps given reason |rejectionReason|:
1309
1317
1. If |event|'s {{AppHistoryNavigateEvent/signal}} is [=AbortSignal/aborted=], then abort these steps.
1310
1318
1. [=Fire an event=] named {{AppHistory/navigateerror}} at |appHistory| using {{ErrorEvent}}, with {{ErrorEvent/error}} initialized to |rejectionReason|, and {{ErrorEvent/message}}, {{ErrorEvent/filename}}, {{ErrorEvent/lineno}}, and {{ErrorEvent/colno}} initialized to appropriate values that can be extracted from |rejectionReason| in the same underspecified way the user agent typically does for the <a spec="HTML">report an exception</a> algorithm.
1311
1319
1. [=Reject=] |transition|'s [=AppHistoryTransition/finished promise=] with |rejectionReason|.
1312
1320
1. If |appHistory|'s [=AppHistory/transition=] is |transition|, then set |appHistory|'s [=AppHistory/transition=] to null.
1313
1321
1. If |ongoingNavigation| is non-null, then [=app history API navigation/reject the finished promise=] for |ongoingNavigation| with |rejectionReason|.
1322
+
1. [=Potentially reset the focus=] given |appHistory| and |event|.
1314
1323
1. Otherwise, if |ongoingNavigation| is non-null, then:
1315
1324
1. Set |ongoingNavigation|'s [=app history API navigation/serialized state=] to null.
1316
1325
<p class="note">This ensures that any call to {{AppHistory/navigate()|appHistory.navigate()}} which triggered this algorithm does not overwrite the [=session history entry/app history state=] of the [=session history/current entry=] for cross-document navigations.
@@ -1325,6 +1334,7 @@ The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps a
1325
1334
<div algorithm>
1326
1335
To <dfn>finalize with an aborted navigation error</dfn> given an {{AppHistory}} |appHistory|, an [=app history API navigation=] or null |ongoingNavigation|, and an optional {{DOMException}} |error|:
1327
1336
1337
+
1. Set |appHistory|'s [=AppHistory/focus changed during ongoing navigation=] to false.
1328
1338
1. If |error| was not given, then set |error| to a [=new=] "{{AbortError}}" {{DOMException}}, created in |appHistory|'s [=relevant Realm=].
1329
1339
1. If |appHistory|'s [=AppHistory/ongoing navigate event=] is non-null, then:
1330
1340
1. Set |appHistory|'s [=AppHistory/ongoing navigate event=]'s [=Event/canceled flag=] to true.
@@ -1360,6 +1370,20 @@ The <dfn attribute for="AppHistoryDestination">sameDocument</dfn> getter steps a
1360
1370
1. For each |traversal| of |traversals|: [=finalize with an aborted navigation error=] given |appHistory| and |traversal|.
1361
1371
</div>
1362
1372
1373
+
<div algorithm>
1374
+
To <dfn>potentially reset the focus</dfn> given an {{AppHistory}} object |appHistory| and an {{AppHistoryNavigateEvent}} |event|:
1375
+
1376
+
1. Let |focusChanged| be |appHistory|'s [=AppHistory/focus changed during ongoing navigation=].
1377
+
1. Set |appHistory|'s [=AppHistory/focus changed during ongoing navigation=] to false.
1378
+
1. If |event|'s [=AppHistoryNavigateEvent/focus reset behavior=] is "{{AppHistoryFocusReset/manual}}", then return.
1379
+
1. If |focusChanged| is true, then return.
1380
+
1. Let |document| be |appHistory|'s [=relevant global object=]'s [=associated Document=].
1381
+
1. Let |focusTarget| be the <a spec="HTML">autofocus delegate</a> for |document|.
1382
+
1. If |focusTarget| is null, then set |focusTarget| to |document|'s <a spec="HTML" lt="the body element">body element</a>.
1383
+
1. If |focusTarget| is null, then set |focusTarget| to |document|'s [=document element=].
1384
+
1. Run the <a spec="HTML">focusing steps</a> for |focusTarget|, with |document|'s <a spec="CSS2">viewport</a> as the fallback target.
1385
+
</div>
1386
+
1363
1387
<h2 id="apphistoryentry-class">App history entries</h2>
1364
1388
1365
1389
<xmp class="idl">
@@ -1735,6 +1759,16 @@ So, update the HTML Standard to always store the policy container on the [=sessi
1735
1759
1736
1760
<h2 id="other-patches">Other patches</h2>
1737
1761
1762
+
<h3 id="focus-patches">Focus tracking</h3>
1763
+
1764
+
To support the {{AppHistoryTransitionWhileOptions/focusReset}} option, the following patches need to be made:
1765
+
1766
+
Update the <a spec="HTML">focusing steps</a> to, right before they call the <a spec="HTML">focus update steps</a>, set the {{Document}}'s [=relevant global object=]'s [=Window/app history=]'s [=AppHistory/focus changed during ongoing navigation=] to true.
1767
+
1768
+
Update the <a spec="HTML">focus fixup rule</a> to additionally set the {{Document}}'s [=relevant global object=]'s [=Window/app history=]'s [=AppHistory/focus changed during ongoing navigation=] to false.
1769
+
1770
+
<p class="note">In combination, these ensure that the [=AppHistory/focus changed during ongoing navigation=] reflects any developer- or user-initiated focus changes, unless they were undone by the focus fixup rule. For example, if the user moved focus to an element which was removed from the DOM while the promise passed to {{AppHistoryNavigateEvent/transitionWhile()}} was settling, then that would not count as a focus change.
1771
+
1738
1772
<h3 id="cancel-navigation">Canceling navigation and traversals</h3>
1739
1773
1740
1774
The existing HTML specification discusses canceling a navigation and traverals in a few places. However, the process is not very well-defined, and per <a href="https://github.com/whatwg/html/issues/6927">whatwg/html#6927</a>, is not very interoperable. We plan to make it more rigorous, after the <a href="https://github.com/whatwg/html/issues/5767">session history rewrite</a> lands.
0 commit comments