diff --git a/source b/source index 657afbe5d4d..609315f1646 100644 --- a/source +++ b/source @@ -3267,6 +3267,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
AbortSignal
User agents are encouraged to implement the features described in execCommand. EXECCOMMAND
-The following parts of Fullscreen API are referenced from this
- specification, in part to define the rendering of dialog
elements, and also to
- define how the Fullscreen API interacts with HTML: FULLSCREEN
The following features are defined in Fullscreen API: FULLSCREEN
requestFullscreen()
requestFullscreen()
fullscreenchange
The close
event will not be fired.
The close()
method, and any user-agent provided cancelation interface, will no longer be able
- to close the dialog.
The close()
method, and any close requests, will no longer be able to close the dialog.
If the dialog was shown using its showModal()
method, the Document
will still be contain this, then add an element
to the top layer given this.
Set this's close watcher to the + result of creating a close watcher given + this's relevant global object, with:
+ +cancelAction being to return the
+ result of firing an event named cancel
at this, with the cancelable
attribute initialized to true.
closeAction being to close the + dialog given this and null.
Set this's previously focused element to the focused element.
The dialog
HTML element removing steps, given removedNode
- and oldParent, are if removedNode's node document's top
- layer contains removedNode, then
- remove an element from the top layer immediately given removedNode.
If removedNode's close watcher is not + null, then:
+ +Destroy removedNode's close watcher.
Set removedNode's close watcher to + null.
If removedNode's node document's top layer contains removedNode, then remove an element from + the top layer immediately given removedNode.
The close(returnValue)
method steps are:
@@ -60591,6 +60625,19 @@ interface HTMLDialogElement : HTMLElement {
Queue an element task on the user interaction task source given the
subject element to fire an event named
close
at subject.
If subject's close watcher is not null, + then:
+ +Destroy subject's close watcher.
Set subject's close watcher to + null.
The HTMLDialogElement
: HTMLElement {
Canceling dialogs: When Document
is
- blocked by a modal dialog dialog, user agents may provide a user interface
- that, upon activation, queues an element task on the
- user interaction task source given the dialog element to run these steps:
-
-
Let close be the result of firing an
- event named cancel
at dialog, with the cancelable
attribute initialized to true.
If close is true, then close the dialog dialog with - null.
An example of such a UI mechanism would be the user pressing the "Escape" key.
- -Each dialog
element has a close watcher,
+ which is a close watcher or null, initially null.
Each dialog
element has an is modal flag. When a dialog
element is created, this flag must be set to false.
In an implementation-defined (and likely device-specific) manner, a user can send + a close request to the user agent. This indicates that the user wishes to close + something that is currently being shown on the screen, such as a popover, menu, dialog, picker, or + display mode.
+ +Some example close requests are:
+ +The Esc key on desktop platforms
The back button or gesture on certain mobile platforms such as Android
Any assistive technology's dismiss gesture, such as iOS VoiceOver's two-finger scrub "z" + gesture
A game controller's canonical "back" button, such as the square button on a DualShock + gamepad.
Whenever the user agent receives a potential close request targeted at a Document
+ document, it must perform the following close request steps:
If document's fullscreen element is not null, then fully exit + fullscreen and return.
+ +This does not fire any relevant event, such as keydown
; it only fires fullscreenchange
.
Fire any relevant events, per UI Events or other relevant specifications.
+ +An example of relevant events in the UI Events model would be
+ the keydown
and keyup
+ sequence that UI Events suggests firing when the
+ user presses the Esc key on their keyboard. On most platforms with keyboards, this
+ is treated as a close request, and so would trigger these close request
+ steps.
An example of relevant events that are outside of the model given in
+ UI Events would be assistive technology synthesizing an Esc keydown
and keyup
sequence when
+ the user sends a close request by using a dismiss gesture.
Let event be null if no such events are fired, or the Event
object
+ representing one of the fired events otherwise. If multiple events are fired, which one is chosen
+ is implementation-defined, except that if keydown
and keyup
events were
+ fired for the Esc key, the user agent must pick the keyup
event.
If event is not null, and its canceled flag is set, then + return.
Let finishSteps be the following steps:
+ +If document is not fully active, then abort these + steps.
Let closedSomething be the result of processing close watchers on document's relevant global + object.
If closedSomething is true, then abort these steps.
Otherwise, there was nothing watching for a close request. The user agent + may instead interpret this interaction as some other action, instead of interpreting it as a + close request.
If event is not null, then perform finishSteps within the same task as + event was fired in, immediately after firing the event. Otherwise, queue a + global task on the user interaction task source given document's + relevant global object to perform finishSteps.
On platforms where Esc is the close request, the user
+ agent will first fire an appropriately-initialized keydown
and
+ keyup
event sequence. If the web developer cancels the keyup
event by calling preventDefault()
, then nothing further happens. But if
+ the event fires without being canceled, then the user agent proceeds to process close
+ watchers.
On platforms where a back button is a potential close request, no + event is involved, so when the back button is pressed, the user agent queues a task to process close watchers. If there is a close + watcher on the close watcher stack, then that will get triggered. If there is + not, then the user agent can interpret the back button press in another way, for example as a + request to traverse the history by a delta of −1.
+ + +Each Window
has a close watcher stack, a list of close watchers, initially empty.
It isn't quite a proper stack, as items can get removed from the middle of it if a + close watcher is destroyed or closed via web developer code. User-initiated close requests always act on the top of the close watcher + stack, however.
+ +A close watcher is a struct with the following items:
+ +A window, a Window
.
A cancel action, a list of steps. These + steps can never throw an exception, and return either true (to indicate that the caller will + proceed to the close action) or false (to + indicate that the caller will bail out).
A close action, a list of steps. These + steps can never throw an exception.
A was created with user activation + boolean.
An is grouped with previous boolean.
An is running cancel action + boolean.
The is grouped with previous boolean + is set if a close watcher is created without transient activation. + (Except the very first close watcher in the close watcher stack that is created + without transient activation, which gets a pass on this restriction.) It causes a user-triggered + close request to close all such grouped-together close watchers, thus ensuring that + web developers can't make close requests ineffective by creating an excessive number of close + watchers.
+ +A close watcher closeWatcher is + active if closeWatcher's window's + close watcher stack contains + closeWatcher.
+ +To create a close watcher given a Window
window, a list of
+ steps cancelAction, and a list of
+ steps closeAction:
Assert: window's associated
+ Document
is fully active.
Let wasCreatedWithUserActivation and isGroupedWithPrevious be + null.
If window has history-action activation, then:
+ +Consume history-action user activation given window.
Set wasCreatedWithUserActivation to true.
Set isGroupedWithPrevious to false.
Otherwise, if there is no close watcher in window's close + watcher stack whose was created with user + activation is false, then:
+ +Set wasCreatedWithUserActivation to false.
Set isGroupedWithPrevious to false.
This will be the one "free" close watcher created without user activation, which + is useful for cases like session inactivity timeout dialogs or notifications from the + server.
+Otherwise:
+ +Set wasCreatedWithUserActivation to false.
Set isGroupedWithPrevious to true.
Let closeWatcher be a new close watcher, with
+ +Append closeWatcher to window's + close watcher stack.
Return closeWatcher.
To request to close a close + watcher closeWatcher:
+ +If closeWatcher is not active, then + return.
If closeWatcher's is running + cancel action is true, then return.
Let window be closeWatcher's window.
If window's associated
+ Document
is not fully active, then return.
If window has history-action activation, then:
+ +Set closeWatcher's is running + cancel action to true.
Let shouldContinue be the result of running closeWatcher's cancel action.
Set closeWatcher's is running + cancel action to false.
If shouldContinue is false, then:
+ +Consume history-action user activation given window.
Return.
The guard on these substeps means that the cancel action only has a chance to prevent us from + continuing onward to the close action when + window has history-action activation. In particular, since these + substeps consume history-action user activation, requesting to closing a close watcher + twice without any intervening user activation will skip these substeps.
+Close closeWatcher.
To close a close watcher + closeWatcher:
+ +If closeWatcher is not active, then + return.
If closeWatcher's window's associated Document
is not fully
+ active, then return.
Destroy closeWatcher.
Run closeWatcher's close + action.
To destroy a close watcher + closeWatcher, remove closeWatcher from + closeWatcher's window's close watcher + stack.
+ +To process close watchers given a Window
window:
Let processedACloseWatcher be false.
While window's close watcher stack is not empty:
+ +Let closeWatcher be the last item in + window's close watcher stack.
Request to close + closeWatcher.
Set processedACloseWatcher to true.
If closeWatcher's is grouped with + previous is false, then break.
Return processedACloseWatcher.
CloseWatcher
interface[Exposed=Window]
+interface CloseWatcher : EventTarget {
+ constructor(optional CloseWatcherOptions options = {});
+
+ undefined requestClose();
+ undefined close();
+ undefined destroy();
+
+ attribute EventHandler oncancel;
+ attribute EventHandler onclose;
+};
+
+dictionary CloseWatcherOptions {
+ AbortSignal signal;
+};
+
+ watcher = new CloseWatcher()
watcher = new CloseWatcher({ signal })
Creates a new CloseWatcher
instance.
If the signal
option is provided, then
+ watcher can be destroyed (as if by watcher.destroy()
) by aborting the given
+ AbortSignal
.
If any close watcher is already active, and the Window
does not
+ have transient activation, then the resulting CloseWatcher
will be
+ closed together with that already-active close watcher in response to any
+ close request. (This already-active close watcher does not necessarily
+ have to be a CloseWatcher
object; it could be a modal dialog
element,
+ or a popover generated by an element with the popover
+ attribute.)
watcher.requestClose()
Acts as if a close request was sent targeting watcher, by first
+ firing a cancel
event, and if that event is not canceled
+ with preventDefault()
, proceeding to fire a
+ close
event before deactivating the close watcher as if watcher.destroy()
was called.
This is a helper utility that can be used to consolidate cancelation and closing logic into
+ the cancel
and close
event
+ handlers, by having all non-close request closing affordances call this method.
watcher.close()
Immediately fires the close
event, and then deactivates
+ the close watcher as if watcher.destroy()
was
+ called.
This is a helper utility that can be used trigger the closing logic into the close
event handler, skipping any logic in the cancel
event handler.
watcher.destroy()
Deactivates watcher, so that it will no longer receive close
events and so that new independent CloseWatcher
+ instances can be constructed.
This is intended to be called if the relevant UI element is torn down in some other way than + being closed.
+Each CloseWatcher
instance has an internal close watcher, which is a
+ close watcher.
The new CloseWatcher(options)
+ constructor steps are:
If this's relevant global object's associated Document
is not fully
+ active, then throw an "InvalidStateError
"
+ DOMException
.
Let closeWatcher be the result of creating a + close watcher given this's relevant global object, with:
+ +cancelAction being to return the
+ result of firing an event named cancel
at this, with the cancelable
attribute initialized to true.
closeAction being to fire an event named close
+ at this.
If options["signal
"] exists, then:
If options["signal
"] is
+ aborted, then destroy closeWatcher.
Add the following steps to options["signal
"]:
Destroy closeWatcher.
Set this's internal close watcher to + closeWatcher.
The requestClose()
method steps are to request to close this's internal
+ close watcher.
The close()
+ method steps are to close this's
+ internal close watcher.
The destroy()
method steps are to destroy this's internal close
+ watcher.
The following are the event handlers (and their corresponding event handler event types) that must be supported,
+ as event handler IDL attributes, by all objects implementing the
+ CloseWatcher
interface:
Event handler | Event handler event type + |
---|---|
oncancel | cancel
+ |
onclose | close
+ |
If one wanted to implement a custom picker control, which closed itself on a user-provided
+ close request as well as when a close button is pressed, the following code shows how
+ one would use the CloseWatcher
API to process close requests:
const watcher = new CloseWatcher();
+const picker = setUpAndShowPickerDOMElement();
+
+let chosenValue = null;
+
+watcher.onclose = () => {
+ chosenValue = picker.querySelector('input').value;
+ picker.remove();
+};
+
+picker.querySelector('.close-button').onclick = () => watcher.requestClose();
+
+ Note how the logic to gather the chosen value is centralized in the CloseWatcher
+ object's close
event handler, with the click
event handler for the close button delegating to that logic by
+ calling requestClose()
.
The cancel
event on CloseWatcher
objects can
+ be used to prevent the close
event from firing, and the
+ CloseWatcher
from being destroying. A typical use case is as follows:
watcher.oncancel = async (e) => {
+ if (hasUnsavedData) {
+ e.preventDefault();
+
+ const userReallyWantsToClose = await askForConfirmation("Are you sure you want to close?");
+ if (userReallyWantsToClose) {
+ hasUnsavedData = false;
+ watcher.close();
+ }
+ }
+};
+
+ For abuse prevention purposes, this event only fires if the page has history-action
+ activation, which will be lost after any given close request. This ensures
+ that if the user sends a close request twice in a row without any intervening user activation,
+ the request definitely succeeds; the second request ignores the cancel
event handler and immediately closes the
+ CloseWatcher
.
Combined, the above two examples show how requestClose()
and close()
differ. Because we used requestClose()
in the click
event handler for the close button, clicking that button will
+ trigger the CloseWatcher
's cancel
event, and thus
+ potentially ask the user for confirmation if there is unsaved data. If we had used close()
, then this check would be skipped. Sometimes that
+ is appropriate, but usually requestClose()
is
+ the better option for user-triggered close requests.
In addition to the user activation restrictions for cancel
events, there is a more subtle form of user activation gating
+ for CloseWatcher
construction. If one creates more than one
+ CloseWatcher
without user activation, then the newly-created one will get grouped
+ together with the most-recently-created close watcher, so that a single close
+ request will close them both:
window.onload = () => {
+ // This will work as normal: it is the first close watcher created without user activation.
+ (new CloseWatcher()).onclose = () => { /* ... */ };
+};
+
+button1.onclick = () => {
+ // This will work as normal: the button click counts as user activation.
+ (new CloseWatcher()).onclose = () => { /* ... */ };
+};
+
+button2.onclick = () => {
+ // These will be grouped together, and both will close in response to a single close request.
+ (new CloseWatcher()).onclose = () => { /* ... */ };
+ (new CloseWatcher()).onclose = () => { /* ... */ };
+};
+
+ This means that calling destroy()
, close()
, or requestClose()
properly is important. Doing so is
+ the only way to get back the "free" ungrouped close watcher slot. Such close watchers created
+ without user activation are useful for cases like session inactivity timeout dialogs or urgent
+ notifications of server-triggered events, which are not generated in response to user
+ activation.
cancel
Event
- dialog
elements, input
elements
- dialog
elements when they are canceled by the user (e.g., by pressing the Escape key), or at input
elements in the File state when the user does not change their selection
+ CloseWatcher
, dialog
elements, input
elements
+ CloseWatcher
objects or dialog
elements when they receive a close request, or at input
elements in the File state when the user does not change their selection
change
@@ -137761,8 +138412,8 @@ INSERT INTERFACES HERE
close
Event
- dialog
elements
- dialog
elements when they are closed
+ CloseWatcher
, dialog
elements
+ CloseWatcher
objects or dialog
elements when they are closed via a close request or via web developer code
connect
@@ -140053,6 +140704,13 @@ INSERT INTERFACES HERE
href="https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document">W3C Software and
Document License.
+ Part of the revision history of the Close requests and close watchers section can be
+ found in the WICG/close-watcher
+ repository, which is available under the W3C Software and
+ Document License.
Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a Creative Commons Attribution 4.0 International License. To the extent portions of it are incorporated into source code, such