Repository navigation
Open
Labels
accessibilityAffects accessibilityAffects accessibilityaddition/proposalNew features or enhancementsNew features or enhancementsneeds implementer interestMoving the issue forward requires implementers to express interestMoving the issue forward requires implementers to express interest
Description
Previously:
- https://www.w3.org/Bugs/Public/show_bug.cgi?id=24983
- https://groups.google.com/forum/#!msg/browser-accessibility-dev/QinGGM_OM7Y/FHpxY_qfBgAJ
Based on these discussions, especially the last one, I'd like to propose the following API to explain some of the magic of <dialog>:
document.blockingElements.push(element); // note: if element is already in the blocking elements stack, it moves to the top
document.blockingElements.pop();
document.blockingElements.remove(element); // see https://github.com/whatwg/html/issues/897#issuecomment-198565716
document.blockingElements.top; // or .current or .peek()We would generalize the "pending dialog stack" (which, note, is only used when showModal() is called, not just show()) to a "blocking element stack".
Dialogs would take part in that stack and be fully explained by it and integrated into it. For example:
- Calling
document.blockingElements.topafterdialogEl.showModal()would returndialogEl. - Calling
document.blockingElements.pop()with an active modal dialog would make that dialog un-modal (but not hide it or remove it from the document), and make whatever was below it on the stack become the new blocking element.
Ccing some people form the browser-accessibility-dev discussion: @marcysutton @alice @minorninth. Implementer interest would be especially helpful in moving this forward, @alice @minorninth.
Reacted by Valdrin Koshi, JC, Chris Rebert, Kevin Lozandier , mantou, Kagami Sascha Rosylight, Laurent Pellegrino, J. S. Choi, Matei Stanca, Matthew Phillips and 17 more
Reacted by JC
Reacted by JC, Laurent Pellegrino, Matei Stanca and klihelp
Reacted by JC, Tarun Garg, Laurent Pellegrino, Matei Stanca, Mu-An Chiou, Jamie Counsell, Pim de Wit and Cory LaViska
Metadata
Metadata
Assignees
Labels
accessibilityAffects accessibilityAffects accessibilityaddition/proposalNew features or enhancementsNew features or enhancementsneeds implementer interestMoving the issue forward requires implementers to express interestMoving the issue forward requires implementers to express interest
Activity
annevk commentedon Mar 18, 2016
It seems @cookiecrook might also have valuable input given he filed the bug.
alice commentedon Mar 18, 2016
Very glad to see the conversation continuing on this, thanks for pushing it forward @domenic!
I like this concept in general. I'd like to dig a little deeper into what an element being on the "blocking element stack" means. Here are my guesses, basically copied from
<dialog>'s behaviour:Have I missed anything?
Also, would the order of the stack determine the visual layering order? If calling
pop()doesn't hide the element, what does it do?alice commentedon Mar 18, 2016
Also, I'd like to ensure that we don't prematurely write off
inertas a primitive which may be useful separately from this concept.domenic commentedon Mar 18, 2016
Thanks for working on the precision there @alice. You're right that there are several separate concepts. Currently (1) is handled by blocked by a modal dialog, which is set in showModal(). (2) is the only part that is directly tied to the definition of the pending dialog stack. In theory we could tease them apart, so that you can block all other elements on the page without being in the top layer, but in practice that's probably not so useful.
(3) is a little strange. See some previous discussion in #744. Currently the spec has all dialogs being control group owner objects. This is meant to cause tabbing to cycle if you're inside the dialog---even if the dialog is non-modal. However, Chrome does not currently do this (and only Chrome implements dialogs). And it's worth noting that for modal dialogs, this doesn't really matter, since everything else being inert means it's not focusable. So maybe we don't need to worry about (3), assuming we are going to do (1). (Which seems very likely, since that was the original ask.)
As long as you are in the stack, you are in the top layer, and the order of the top layer is indeed determined by the order of the stack. (Maybe there are issues here with fullscreen, though, that make it nontrivial to just map the ordering?) But once you are popped from the stack, you are no longer in the top layer. You will be below everything inside the top layer, but still visible, unless you change your visibility (e.g. with CSS).
alice commentedon Mar 18, 2016
Seems fair to me. I'm still struggling with what a non-modal dialog looks like (and what is a dialog group?)
Yep, this works for me too. I definitely think tying (1) and (2) together is a good idea - the visual cue is an intrinsic part of the behaviour.
Still thinking about naming, by the way, but I don't want to sidetrack into that discussion until we're all on the same page with the core concepts (not least because I think they will affect what nomenclature makes sense).
domenic commentedon Mar 18, 2016
An example of a non-modal dialog is at https://jsbin.com/tojolefele/edit?html,output (Chrome only). Per spec, once you pop open the dialog and focus the button labeled "dialog 1", your tabbing should cycle between "dialog 1" and "dialog 2" and not break out to the "1" and "2" and "Run with JS" buttons. (Unless, presumably, you used something like Esc or other keyboard shortcuts to pop up outside the dialog's control group.)
Dialog groups appear to be a rather confusing concept meant to express "all dialogs in a control group", to handle cases like a
Documentwith multiple open dialogs? Focus fixup rule two and three seem most relevant...alice commentedon Mar 18, 2016
@domenic If the non-modal dialog did trap focus, what would the difference be between that and a modal dialog? i.e. what is the purpose/function of a non-modal dialog, if it is indistinguishable from a bug?
domenic commentedon Mar 18, 2016
You can still activate (and focus) things that are outside the non-modal dialog, e.g. with the mouse most obviously, but also with the keyboard assuming your UA allows you to escape a focus group using a key like Esc or ctrl + up arrow or similar. It only affects normal sequential focus navigation (i.e. the tab key).
alice commentedon Mar 18, 2016
That seems like pretty weird behaviour, but I'm going to assume it makes sense for reasons I just don't currently understand.
To get back to the current topic, then:
document.blockingElements, basically as if callingdialog.openModal()calleddocument.blockingElements.push(dialog).document.blockingElements.remove(dialog)(assuming such an API exists)document.blockingElements.pop()if the top blocking element is a modal<dialog>? This seems ill defined currently - an open<dialog>must be part of the top layer, butpop()shouldn't automatically close it, as I understand it.domenic commentedon Mar 18, 2016
Yep
Ah, interesting; it's not always the top, so such an API would be necessary to fully explain dialogs. We could add that... although we need to define what happens if you push the same element twice. I guess it gets moved to the top, instead of creating two entries.
It would remove it from the top layer, but it would still be visible, since you didn't actually change its visibility (e.g. with CSS).
If there is something else in the top layer then the dialog would be hidden underneath it.
domenic commentedon Mar 18, 2016
In other words, it would transition the dialog to exactly the same state it would be in if you just called .show(), instead of .showModal().
alice commentedon Mar 18, 2016
Ah - I'd missed that non-modal dialogs weren't in the top layer. Makes perfect sense in that case.
Yep, I think that makes a lot of sense.
I think
remove()also just makes sense if there is any case where a blocking element can become non-blocking while it's not the top blocking element.domenic commentedon Mar 18, 2016
Edited the OP to include
.remove()and to note that when you.push()an element that is already in the stack, it moves to the top.13 remaining items
cookiecrook commentedon Mar 24, 2016
@domenic wrote:
Your proposal's good. Don't abandon it. We're trying to help you make it better, not bigger.
cookiecrook commentedon Mar 24, 2016
@alice wrote:
I can let the other points rest if this one is picked up. Authors wanting to focus something else could capture the onblur or ondomfocusout events on the popped layer and explicitly set focus() elsewhere.
@domenic wrote:
It's unrealistic to assume this will happen automatically. Focus is an inconsistent mess across all browsers because of the poor specification in HTML. Case in point: if the focused element is ever removed from the view, focus falls back to the body element (the nuclear option). Most mouse users are happily oblivious, but this behavior is only acceptable to people who don't use or understand focus.
annevk commentedon Mar 24, 2016
@cookiecrook your last paragraph seems to make two points. 1) Focus is not well defined. It's not clear whose job it is to define focus but I guess HTML tries to do most of it so we should probably keep making improvements there. 2) The behavior that is defined isn't ideal for users that depend on focus navigation.
We can fix 1, especially if you help out with tests and maybe even PRs. Unfortunately we lack the necessary manpower to clean everything up at once, but I think we all agree user interface events could use some help.
It's unclear to me whether we can fix 2 without introducing new features, since focusing the body as backup has been the behavior for so long, it's likely sites depend on it.
cookiecrook commentedon Mar 24, 2016
I realize now another core purpose of my last paragraph was unclear. It's unrealistic to assume blocking stack focus will happen automatically because what will happen is what has always happened:
Hopefully that's more clear. Any API (even a primitive) that can cause document focus to be lost—as this API can with regards to inertness—should take care to avoid common focus mistakes. In this case, an explicit one-liner to restore the previously "focused element" and the "sequential focus navigation starting point" in the stack may be sufficient.
@annevk wrote:
Understood. I don't peruse the specs as often as I used to, but I will file bugs and/or PRs when I see them. On a related note: I just went looking for the only HTML spec focus bug I remembered offhand, and I'm pleased to see the following was added to the spec re: fragment navigation.
I remember arguing with Ian over this point on numerous occasions. Congrats to whomever made the edit or convinced another editor.
alice commentedon Mar 24, 2016
Regarding this proposal: I think we actually have some consensus at this point between @domenic, @cookiecrook and I.
I spoke with Domenic offline to clarify his "falls out automatically" comment, and he pointed to https://html.spec.whatwg.org/multipage/interaction.html#focus-fixup-rule-three - "Let new focus target be the currently focused area of a top-level browsing context." etc. I think the source of the misunderstanding may have been in part that Chrome's implementation of
<dialog>currently does not implement this behaviour.Domenic and I also agreed that we can and should generalise this up the blocking element stack [edit: my reading of the "focus fixup" language is that this should already happen] - but without, at least immediately, exposing that state information to developers.
cookiecrook commentedon Mar 24, 2016
Thanks for the link. If this blocking element API will reference the "focus fix-up" rules, that may be sufficient. Ultimately I'd like to see more, but I agree we're in consensus for baseline focus support.
upsuper commentedon Nov 23, 2016
How would it interact with the fullscreen stack? Should fullscreen element also be put into this stack as well? If so, a stack containing fullscreen elements and modal dialogs sounds similiar to the existing top layer concept. Should these two share the same stack somehow?
domenic commentedon Nov 23, 2016
I don't think it's related to the fullscreen stack.
upsuper commentedon Nov 23, 2016
Shouldn't fullscreen element block all other elements as well?
domenic commentedon Nov 23, 2016
I see, I had forgotten the context. Yes, this is all tied in to the top layer concept. Probably it is best to manipulate that directly, but it does mean that using the fullscreen APIs could manipulate the stack out from under you.
upsuper commentedon Nov 23, 2016
Yeah, I think that could be an issue for dialog as well. Probably adding a flag to items of blocking element stack indicating how an element is added, and forbid script from removing an element from the stack if it is added by the Fullscreen API or
dialog,showModal?valdrinkoshi commentedon Apr 12, 2017
Elements in the Top Layer should be subject to the Top Layer's stacking context, in other words rendered on top of everything else - even if they're declared inside an element that creates new stacking context.
This would solve issues like this one http://jsbin.com/kuboqa/1/edit?html,output
^ you cannot click the button in
.overlaybecause stacking contextdomenic commentedon Apr 12, 2017
Yes, that is literally the definition of top layer.
valdrinkoshi commentedon Jun 28, 2017
Regarding multiple Top Layer elements opened, I wanted to provide the following use case (try it on chrome http://jsbin.com/bawecix/5/edit?html,output):
We have 3 overlays:
<img>tooltip,<dialog>and<select>. All render their contents on the Top Layer.Regarding the interactions, the last opened overlay is the first to be notified of events - e.g.
node1,node2, thennode3listen forclickevent ->node3event listener is invoked first.In the above example, only
<dialog>and<select>take action onclick/keydownevents.keydownevent propagation (that is to say, only<select>closes when you press Escape)clickevent propagation (<select>closes on click)<select>prevents scrolling from happening when it's opened,<dialog>doesn'tIf I were to implement this, I'd have to implement my own LIFO event listener system to delegate events to the right overlay (here an example).