-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Current state
close()
on a closed<dialog>
.show()
on an open<dialog>
, unless that dialog is in the popover showing state.
…do not throw.
Whereas:
showModal()
on an open dialogshow()
on an open dialog that is also in the popover showing state.showPopover()
on an open popoverhidePopover()
on a closed popover
…throw an error.
So the current state of things is that dialog is inconsistent with itself, and popover is inconsistent with dialog.
Elsewhere on the platform
Looking at other cases of hide/remove/delete in the web platform, none of these throw:
new Set().delete('foo')
('hello').replace('world', '')
[].pop()
document.createElement('div').removeAttribute('foo')
document.createElement('div').remove()
document.createElement('div').classList.remove('foo')
document.createElement('div').removeEventListener(() => {})
document.createElement('div').classList.replace('foo', 'bar')
(although it does returnfalse
)
Counter examples:
el.removeChild(otherEl)
throws ifotherEl
is not a child ofel
.el.removeAttributeNode(attributeNode)
throws ifattributeNode
is not an attribute node ofel
.document.exitFullscreen()
throws if no element is fullscreen.
removeChild
and removeAttributeNode
are pretty old APIs that developers tend to avoid in favour of friendlier equivalents.
exitFullscreen
is the most interesting example, since it's related to top level.
Looking at 'add' cases:
new Set(['foo', 'bar']).add('foo')
el.classList.add('foo')
el.addEventListener(callback)
All behave set-like. As in, if the item is already in the set, it doesn't throw, and it doesn't change the order of items. It's a no-op.
el.requestFullscreen(options)
on an already-fullscreen element will adapt to changes in options
, but it will not fire a fullscreenchange
event.
el.append(otherEl)
will remove otherEl
from its parent, and add otherEl
as the last child of el
. This happens even if otherEl
is already the last child of el
, and it's observable in a bunch of ways, including mutation observers. But this is specifically 'append', not 'add'.
The current mindset in the frameworks world is to let developer declare the state they want, and the framework figures out what needs to change to get to that state. None of the frameworks throw if the developer re-declares the current state.
Proposal for <dialog>
It's pretty weird that show()
followed by showModal()
will throw, whereas showModal()
followed by show()
will no-op.
Option 1: 'show' throws if already shown
show()
or showModal()
followed by show()
or showModal()
will throw.
This is at least consistent between the two methods, but it doesn't seem to fit with the majority of the platform. You could say the behaviour here is justified in being unusual due to the different ways a dialog can be shown, but that wouldn't be consistent with requestFullscreen(options)
either.
Option 2: 'show' is a no-op if already shown
With show()
or showModal()
followed by show()
or showModal()
, the second call will be a no-op.
This is at least consistent between the two methods, but it seems weird that showModal()
would no-op resulting in a not-modal dialog.
Option 3: Update the type of 'show'
show()
followed by showModal()
will 'upgrade' the dialog to a modal dialog.
showModal()
followed by show()
will 'downgrade' the dialog to a non-modal dialog.
This feels consistent with how requestFullscreen(options)
will react to changes in options
even if the element is already fullscreen.
Option 4: showModal()
is a more specific version of show()
show()
followed by showModal()
will 'upgrade' the dialog to a modal dialog.
showModal()
followed by show()
is a no-op, because the dialog is already shown.
This seems less flexible, but it feels like it makes sense. We could add showModeless()
in future to do the more specific thing, or add an option like show({ mode: 'modeless' })
.
Proposal for popover
I think we should match the majority of the platform, and open-when-already-open, and close-when-already-closed, should not throw.
Option 1: Second showPopover()
moves popover to the top
This could be achieved by hiding then re-showing the popover, which would be like el.append()
. If the developer is calling showPopover()
, it seems like they feel this popover is important at this time, which suggests move-to-top is the right thing to do.
If this makes sense, showModal
on <dialog>
should do the same.
Option 2: Second showPopover()
is a no-op
That's consistent with most of the platform.
Proposal for popover on <dialog>
Option 1: <dialog>
cannot popover
Similar to requestFullscreen
, calling showPopover()
on a <dialog>
will always throw, even if the dialog isn't open. That removes the overlap between these two features, and it's consistent with fullscreen.
Option 2: Avoid both features being active at the same time
- If
show()
is not a no-op, then it should throw if the element is in the popover showing state. - If
showModal()
is not a no-op, then it should throw if the element is in the popover showing state. - If
showPopover()
is not a no-op, then it should throw if the element is a dialog, and open.
I'm not sure this complexity is worth it, and it's inconsistent with requestFullscreen
.