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
{{ message }}
Repository navigation
Move the focus back to the previous focused element for dialog.close() #5678
What if that element has been made un-focusable (removed, made inert, etc.)? What about if it has moved to another location or reused for a different purpose entirely (e.g. via a virtual DOM framework)?
In general, this would be the first time that the specification focuses an element without explicit web developer request or user interaction, so it raises all sorts of unprecedented questions.
Using https://software.hixie.ch/utilities/js/live-dom-viewer/ I played with variants on <input oninput="alert(1); document.body.appendChild(this); w(document.activeElement);"> to figure out what might be reasonable solutions to those problems. It seems that in general browsers fallback to the body element (the document in the model, iirc).
And is it really that different from showing the dialog running focusing steps? (Note that I'm not sure this should be limited to close(). It seems like something that would happen for dismissal in general.)
Yes, it is true that this re-focusing of the element that triggered the dialog is true for any kind of closing the dialog. See also the WAI-ARIA design pattern for role "dialog". It could be argued that dialog is a complex enough compound widget, with modality, anyway, so this is the first of its kind in the HTML specification, and it would also be the first of its kind to request that focus be set back to the triggering element if still possible. The alternative would be the body element, which would require keyboard users to tab a cillion times to get back to the element that triggered the dialog, and completely lose context in the meantime. It would eliminate a very common accessibility mistake if browsers would do this automatically if the triggering element is still focusable.
Reacted by Scott O'Hara, Becky Gibson, Clint Fisher and Kostiantyn Ko
It's always been recommended that authors move focus back to the "trigger" element (the element whose action caused the dialog or menu to be displayed). This is is consistent with platform conventions, too. For example, on popup buttons like <select>, the focus is moved back to the button after a selection has been made (Space/Enter/Click), or when the menu has been dismissed (Escape).
However, perhaps it's too simple to spec the "trigger" as the "previously focused element"? I think most of the time, the previously focused element is the same as the trigger, but there may be some scenarios where it's not. For example, if something is clicked and doesn't received focus from the click, then the "previously focused element" would be whatever was focused before the click. I don't know how common it would be. I'm just trying to consider all scenarios.
(Updated: Re-posting this musing as a standalone comment to make it clear it's not an official WebKit position.)
In line with that, perhaps this "focus the previously focused element" idea should not be tied to the <dialog> element at all. It seems like a focus stack could be associated with the inertness primitive. A lot of JavaScript frameworks implement focus container stacks (I've helped with a few) so there is definitely a common use case outside <dialog>.
It's very strange that close event is async. Since we're already mutating the content attribute which would cause mutation events to fire in all major browsers, there doesn't seem to be any security / integrity benefit either.
@alice@mfreed7@smfr Do you think changing the close event to a sync event to allow authors to change the focus is a reasonable approach here? I am happy to propose the spec changes.
It doesn’t sound like anyone is opposing making the close event sync instead of async, so I suppose I could implement it in chrome along with the other focus change proposed here.
I don’t know why it is async and I don’t know if changing it will break any websites, but I could say the same thing about the proposed focus change and I won’t be sure until I try.
The usual danger with sync events is outlined in https://w3ctag.github.io/design-principles/#guard-against-recursion . I'm not sure whether any of that reasoning applies here; I think it would require code like dialog.onclose = () => { dialog.open(); dialog.close(); } which is probably not something we really need to guard against.
(There is a small risk with regards to eventually removing support for mutation events (or changing when they fire, at least), but if we manage that we could certainly move this to CEReactions timing then too, I'd think.)
While implementing this in chrome, it was pointed out that changing focus synchronously, which fires an event, will make us have to write the code somewhat carefully (making sure that changing focus is the very last thing we do).
Was it ever considered to change focus asynchronously, or would that break anything? Am I missing anything on this topic?
Is it materially different from calling focus()? Given that screen reader experience is defined by what has focus, keeping the focus on a closed dialog might not yield the best results, I'd think.
The intent to manage focus of a closing dialog is a good one. However, if
authors have a clear understanding of the process they should have the
ability to change the focus target on dialog.close().
A common example: a dialog created for the "confirm delete" action with
"confirm" and "cancel" buttons. Cancel can target the default element per
this spec (i.e., the "delete" button). Confirm would send focus to
body since the deleted item no longer exists. An author should have the
ability to override this behavior but programmatically setting focus could
introduce a race condition with the default behavior.
Possible solutions:
the addition of a dialog property, with an idref value, that will
override this behavior by targeting the specified element when present
provided the element exists and is connected to the dom.
an options object or flag passed to dialog.close() that overrides the
default behavior.
Activity
domenic commentedon Jun 25, 2020
What is "the previously focused element"?
sefeng211 commentedon Jun 25, 2020
I mean the element that was focused before the dialog showed up.
domenic commentedon Jun 25, 2020
What if that element has been made un-focusable (removed, made inert, etc.)? What about if it has moved to another location or reused for a different purpose entirely (e.g. via a virtual DOM framework)?
In general, this would be the first time that the specification focuses an element without explicit web developer request or user interaction, so it raises all sorts of unprecedented questions.
annevk commentedon Jun 25, 2020
Using https://software.hixie.ch/utilities/js/live-dom-viewer/ I played with variants on
<input oninput="alert(1); document.body.appendChild(this); w(document.activeElement);">to figure out what might be reasonable solutions to those problems. It seems that in general browsers fallback to the body element (the document in the model, iirc).And is it really that different from showing the dialog running focusing steps? (Note that I'm not sure this should be limited to
close(). It seems like something that would happen for dismissal in general.)MarcoZehe commentedon Jun 26, 2020
Yes, it is true that this re-focusing of the element that triggered the dialog is true for any kind of closing the dialog. See also the WAI-ARIA design pattern for role "dialog". It could be argued that dialog is a complex enough compound widget, with modality, anyway, so this is the first of its kind in the HTML specification, and it would also be the first of its kind to request that focus be set back to the triggering element if still possible. The alternative would be the body element, which would require keyboard users to tab a cillion times to get back to the element that triggered the dialog, and completely lose context in the meantime. It would eliminate a very common accessibility mistake if browsers would do this automatically if the triggering element is still focusable.
annevk commentedon Jun 26, 2020
As for implementer interest, Mozilla is interested.
@hober @cookiecrook there was some talk about WebKit eventually adopting
<dialog>too, would you all like to see this changed too?@mfreed7 what about Chrome?
cookiecrook commentedon Jun 26, 2020
It's always been recommended that authors move focus back to the "trigger" element (the element whose action caused the dialog or menu to be displayed). This is is consistent with platform conventions, too. For example, on popup buttons like
<select>, the focus is moved back to the button after a selection has been made (Space/Enter/Click), or when the menu has been dismissed (Escape).However, perhaps it's too simple to spec the "trigger" as the "previously focused element"? I think most of the time, the previously focused element is the same as the trigger, but there may be some scenarios where it's not. For example, if something is clicked and doesn't received focus from the click, then the "previously focused element" would be whatever was focused before the click. I don't know how common it would be. I'm just trying to consider all scenarios.
cookiecrook commentedon Jun 26, 2020
As for WebKit's opinion, there has been support expressed for the
inertattribute but<dialog>is still under consideration.cookiecrook commentedon Jun 26, 2020
(Updated: Re-posting this musing as a standalone comment to make it clear it's not an official WebKit position.)
In line with that, perhaps this "focus the previously focused element" idea should not be tied to the
<dialog>element at all. It seems like a focus stack could be associated with the inertness primitive. A lot of JavaScript frameworks implement focus container stacks (I've helped with a few) so there is definitely a common use case outside<dialog>.17 remaining items
cookiecrook commentedon Aug 28, 2020
cc @smfr
annevk commentedon Sep 10, 2020
@alice @mfreed7 @cookiecrook @smfr ping. Would be nice to make progress here!
rniwa commentedon Sep 11, 2020
It's very strange that
closeevent is async. Since we're already mutating the content attribute which would cause mutation events to fire in all major browsers, there doesn't seem to be any security / integrity benefit either.sefeng211 commentedon Nov 18, 2020
@alice @mfreed7 @smfr Do you think changing the
closeevent to a sync event to allow authors to change the focus is a reasonable approach here? I am happy to propose the spec changes.josepharhar commentedon Dec 7, 2020
It doesn’t sound like anyone is opposing making the close event sync instead of async, so I suppose I could implement it in chrome along with the other focus change proposed here.
I don’t know why it is async and I don’t know if changing it will break any websites, but I could say the same thing about the proposed focus change and I won’t be sure until I try.
Are there any WPTs for this behavior yet?
domenic commentedon Dec 7, 2020
The usual danger with sync events is outlined in https://w3ctag.github.io/design-principles/#guard-against-recursion . I'm not sure whether any of that reasoning applies here; I think it would require code like
dialog.onclose = () => { dialog.open(); dialog.close(); }which is probably not something we really need to guard against.annevk commentedon Dec 7, 2020
(There is a small risk with regards to eventually removing support for mutation events (or changing when they fire, at least), but if we manage that we could certainly move this to CEReactions timing then too, I'd think.)
Move the focus to the previously focused element for dialog.close()
Move the focus to the previously focused element for dialog.close()
josepharhar commentedon May 4, 2021
While implementing this in chrome, it was pointed out that changing focus synchronously, which fires an event, will make us have to write the code somewhat carefully (making sure that changing focus is the very last thing we do).
Was it ever considered to change focus asynchronously, or would that break anything? Am I missing anything on this topic?
annevk commentedon May 5, 2021
Is it materially different from calling
focus()? Given that screen reader experience is defined by what has focus, keeping the focus on a closed dialog might not yield the best results, I'd think.AutoSponge commentedon May 12, 2021
The intent to manage focus of a closing dialog is a good one. However, if
authors have a clear understanding of the process they should have the
ability to change the focus target on dialog.close().
A common example: a dialog created for the "confirm delete" action with
"confirm" and "cancel" buttons. Cancel can target the default element per
this spec (i.e., the "delete" button). Confirm would send focus to
body since the deleted item no longer exists. An author should have the
ability to override this behavior but programmatically setting focus could
introduce a race condition with the default behavior.
Possible solutions:
override this behavior by targeting the specified element when present
provided the element exists and is connected to the dom.
default behavior.
annevk commentedon May 12, 2021
@AutoSponge thank you! Could you file that as a new issue please? Comments in closed issues tend to get lost.
Move the focus to the previously focused element for dialog.close()