Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Have some way of opening <dialog> elements without JavaScript #3567

Open
keithamus opened this issue Mar 14, 2018 · 62 comments
Open

Have some way of opening <dialog> elements without JavaScript #3567

keithamus opened this issue Mar 14, 2018 · 62 comments
Labels
accessibility Affects accessibility addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: dialog The <dialog> element. topic: forms

Comments

@keithamus
Copy link
Contributor

dialog elements are a great addition and I'm glad they're getting implemented, but a key part of their functionality relies on JavaScript: to open a <dialog> you need to use JavaScript to set the open attribute.

It'd be great if there was a way to make a <a> or a <button> elements capable of opening dialogs.

Precident already exists for page interactivity baked into HTML - for example <a href="#.."> can already scroll the page, and <details> elements are capable of hiding elements behind interactivity, so I think it stands to reason <dialog> elements could be opened by other page elements.

@annevk
Copy link
Member

annevk commented Mar 14, 2018

Yeah, it's somewhat weird you can close them via <form method=dialog>, but not open them in any way.

@annevk annevk added addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: forms labels Mar 14, 2018
@guest271314
Copy link
Contributor

One workaround would be to utilize <input type="checkbox"> with an associated <label> element styled as a button and toggle display of the <dialog> element

<!DOCTYPE html>
<html>
<head>
  <style>
    #dialogInput, #dialogInput:checked ~ #dialog {
      display: none;
    }
    [for="dialogInput"] {
      appearance: button;
      -webkit-appearance: button;
      -moz-appearance: button;
      padding: 2px;
    }
  </style>
</head>
<body>
  <label for="dialogInput">Dialog</label>
  <input type="checkbox" id="dialogInput">
  <dialog id="dialog" open>
    <p>Hello universe</p>
  </dialog>
</body>
</html>

or to include <dialog> element as child of <details> element at HTML.

@domenic
Copy link
Member

domenic commented Mar 17, 2018

I don't think there's a very significant difference between e.g. <button opendialog="mydialog"> and <button onclick="mydialog.open()">. That is, I think HTML already has the ability to open dialogs.

@keithamus
Copy link
Contributor Author

IMO there are very specific and quite substantial differences with those two examples. For one, the latter cannot be used in combination with CSP headers that disable inline JavaScript. Secondly the latter example cannot be easily statically analysed - this leads to plenty of problems such as no way for accessibility tools to determine which dialog that button will open.

@annevk annevk added the accessibility Affects accessibility label Mar 17, 2018
@muan
Copy link
Member

muan commented Sep 6, 2018

FWIW, we ended up creating <details-dialog>. Which as the name suggests, is a dialog dependent on details. It is now used across github.com. This was prompted by two reasons:

  1. Needing a way for dialog to be opened without JavaScript as Keith mentioned
  2. Current state of (the lack of) browser support for <dialog>

It does still need JavaScript to be fully accessible, but it is interactive without. Having a native way to achieve the stacking context would definitely be much preferred, whatever it end up being.

@Link2Twenty
Copy link

Link2Twenty commented Jan 3, 2019

@Malvoz a toggle would be helpful but I think it's also worth being able to choose the state you're aiming for.

I think it's worth noting the <dialog> actually has 3 states.

  • open
  • openModal
  • close

though you could argue modal should be an attribute of the dialog and not a method of opening it.

@Link2Twenty
Copy link

Currently, I do something like this

<button data-for-dialog="testDialog" open>Open dialog</button>
<dialog id="testDialog">
  <h1>An example of a native Dialog element</h1>
  <p>This is a dialog box and, believe it or not, it's built into HTML, sure we needed some javascript to open it but hey, it's a start.</p>
  <button data-for-dialog="testDialog" close>Close dialog</button>
</dialog>

and then have some JS like so

const dialogTriggers = document.querySelectorAll(`[data-for-dialog]`);

for (let trigger of dialogTriggers) {
  const dialog = document.getElementById(trigger.dataset.forDialog);
  trigger.addEventListener('click', () => {
    trigger.hasAttribute('open') && dialog.showModal();
    trigger.hasAttribute('close') && dialog.close();
  });
}

I think it would be helpful to be able to do something like this and have it work.

<button for-dialog="testDialog" open>Open dialog</button>
<dialog modal id="testDialog">
  <h1>An example of a native Dialog element</h1>
  <p>This is a dialog box and, believe it or not, it's built into HTML, sure we needed some javascript to open it but hey, it's a start.</p>
  <button for-dialog="testDialog" close>Close dialog</button>
</dialog>

Where you have the desired state is an atribute of your controller, or it toggles if nothing if present.
And where the dialog is a modal or not depending on an attribute on the dialog not how it's opened.

@atk
Copy link

atk commented Jan 4, 2019

I would propose the following addition to the standard: an opens, opensModal and closes attribute that can be added to any HTML element and takes the id of an element to add to or remove from the open/openModal attribute; when both an opening and close attribute are set on the same element, it will perform as a toggle switch.

This will be useful for both existing (details/summary, dialog) as well as future interactive elements that can be opened/closed. A polyfill will also be pretty simple:

if ('opens' in document.createElement('button')) {
    document.addEventListener('click', (ev) => {
        let target = ev.target;
        while (target) {
            if (!target.getAttribute) {
                continue;
            }
            const targetCloses = target.getAttribute('closes');
            const targetOpens = target.getAttribute('opens');
            const targetOpensModal = target.getAttribute('opensModal');
            const targetOpensAny = targetOpens || targetOpensModal;
            const attribute = targetOpensModal ? 'openModal' : 'open';
            if (targetOpensAny && targetOpensAny === targetCloses) {
                const toggle = document.getElementById(targetOpensAny);
                if (toggle.hasAttribute(attribute)) {
                    toggle.removeAttribute(attribute);
                } else {
                    toggle.setAttribute(attribute, true);
                }
            } else {
                if (targetOpensAny) {
                    const open = document.getElementById(targetOpensAny);
                    open.setAttribute(attribute, true);
                }
                if (targetCloses) {
                    const close = document.getElementById(targetCloses);
                    close.removeAttribute('open');
                    close.removeAttribute('openModal')
                }
            }
            target = target.parentElement;
        }
    });
}

@domenic
Copy link
Member

domenic commented Jan 4, 2019

I appreciate all these proposals; however, I think it's more likely that we'll end up removing the dialog element entirely, as it only has a single implementer and is thus retrospectively failing to meet the criteria of https://whatwg.org/working-mode#additions. Given this, proposals to expand an already-failing feature are unlikely to get much engagement...

@Link2Twenty
Copy link

I believe Firefox has the feature behind a flag but were waiting to see how inert progressed before going live.

@domenic
Copy link
Member

domenic commented Jan 4, 2019

That does't match my understanding, but be that as it may, stalled implementations don't really help a feature make progress either.

@KyLeggiero
Copy link

I've had success implementing much of what I need dialog to be by just using CSS, so I don't see much reason to consider this worth abandoning.

@atk
Copy link

atk commented Jan 22, 2019

Implementing this in CSS is possible, but doing so in an accessible way is really difficult to get right. So a semantic element making this easier would be an improvement over the current state.

While I agree that the proposal for dialog is lacking, the idea of having a semantic element for notifications, modals, etc. should not be dismissed easily.

@kshep92
Copy link

kshep92 commented Jan 29, 2019

I honestly don't understand what's the delay in implementing this. I have hardly worked on a project where I didn't need to create a dialog and resorted to custom JS/CSS. A dialog with custom HTML content should be part of the spec and should have been part of the spec since 2013.

@domenic
Copy link
Member

domenic commented Jan 29, 2019

A dialog with HTML content is indeed part of the spec. Putting things in the spec however does not magically compell browsers to implement it. Only Chrome has implemented it so far, and other browsers have shown no movement toward doing so in some time. Your exasperation is probably better directed toward their bug trackers, instead of toward the bug tracker of a specification that you seem to have not read.

@Malvoz
Copy link
Contributor

Malvoz commented Mar 1, 2019

FYI; there has been some movement from Firefox in implementing <dialog> quite recently:
https://bugzilla.mozilla.org/show_bug.cgi?id=840640

@domenic
Copy link
Member

domenic commented Mar 1, 2019

I think you may be over-optimistic there. The last time any code related to dialog landed was a month ago, in which someone added a pref to enable the incomplete implementation, but it was then immediately backed out by bots. The last time someone actually landed implementation code was 2016-12-11, if I am reading correctly. This is not the sort of implementation interest that marks a sustainable feature :(.

@mfranzke
Copy link

mfranzke commented Jan 4, 2020

I think you may be over-optimistic there. The last time any code related to dialog landed was a month ago, in which someone added a pref to enable the incomplete implementation, but it was then immediately backed out by bots. The last time someone actually landed implementation code was 2016-12-11, if I am reading correctly. This is not the sort of implementation interest that marks a sustainable feature :(.

Apart from the previous Firefox news, even also the WebKit teams explicitly mention it in their roadmap 2020 draft .

@Malvoz
Copy link
Contributor

Malvoz commented Jan 25, 2021

MS has a proposal for a <popup> element with the same consideration of being operable without JS in MicrosoftEdge/MSEdgeExplainers#437.

Edit: see the updated Showing Popups.
https://open-ui.org/components/popup.research.explainer#:~:text=Option%20A:%20the%20popup%20attribute

It seems there's implementor interest from WebKit, what's the status of implementing dialog in Firefox (@annevk, @emilio)?

@emilio
Copy link
Contributor

emilio commented Jan 25, 2021

For an official position it's better to reach out at https://github.com/mozilla/standards-positions/issues/new.

On a personal level, it seems nice, though I'm interested in how it interacts with CSS (specially with what happens if a popup is under a transform or such, for example).

Firefox's native popup menus are a somewhat interesting prior art, I think: we have an internal display: -moz-popup, and a couple internal elements to implement them, but they're basically implemented using web tech. Of course they are not quite the same since these actually map to an actual OS widget, and we don't need to deal with the full generality of the web, but feel free to reach out if you're interested in details about those or what not.

@inopinatus
Copy link

Firefox's <dialog> support is now looking great in nightly builds. I've found many development discussions are simplified by this element. Thus, anecdotally, having <dialog> in the standard is a timesaving godsend.

My feedback from using <dialog> echoes the opening motivation, viz. that the omission of open by activation behaviour, rather than JS, feels like an inconsistency. Evidence: we already have mechanisms by which

My suggested approach differs, however; I'd sidestep burdening <a> or <button> with yet more mechanism-specific behaviour, and all the ambiguous complexity this might bring, and instead lean on the <label> element that already proxies activation behaviour; thus amending <dialog> rather minimally such that:

a) it is a labelable element,
b) the activation behaviour is equivalent to invoking show(),
c) it has a boolean attribute, modal, and
d) when the modal attribute is true, the action of show() becomes: run the showModal() steps instead.

Or to put it informally, I think writing <label for="dg0"> as the clickable element to open <dialog id="dg0" modal> is an easy story to tell.

@strowbeary
Copy link

just as button has a form and formaction attribute, it should have a dialog and dialogaction attribute that could make the dialog show or hide.

<dialog id="my-dialog"></dialog>

<button dialog="my-dialog" dialogaction="open">
<button dialog="my-dialog" dialogaction="close">

I think that would be more coherent with existing button attributes.

@Link2Twenty
Copy link

I assumed modal would mean "modal when shown". And "shown" would still need to be done by JS or some equivalent to popovertarget.

That was my intention, yes.

@scottaohara
Copy link
Collaborator

that's fair. it would create a potential mismatch for instances where the dialog was made to render, and had the attribute, but also did not behave as the attribute would otherwise communicate it to behave... but this would not be new to HTML. there are many instances of where the attribute of an element vs its actual state do not align. shrug.

@jpzwarte
Copy link

Combo of popover with dialog. This uses popover="hint" which is not in the first spec, but probably will follow later.
So this use case is that on hover you get a tooltip, but when you click the button, you get the modal dialog.

<button dialogtarget="dialog" popovertarget="tooltip">Click</button>

<div id="tooltip" popover="hint" class="tooltip">I appear on hover</div>

<dialog id="dialog" modal>
  <h1>I am modal</h1>
</dialog>

I don't know about the modal attribute and if that won't break existing apps that use the attribute already for styling purposes.

@mfreed7
Copy link
Collaborator

mfreed7 commented Jun 1, 2023

I'd like to propose we add a similar mixin for dialogs:

interface mixin DialogInvokerElement {
  [CEReactions] attribute Element? dialogTargetElement;
  [CEReactions] attribute DOMString dialogTargetAction;
};

Where dialogTargetAction must be one of the following values 'show'|'hide'|'toggle'|'showModal'|'hideModal'|'toggleModal'.

I would like to +1 this proposal. It is almost perfectly parallel to popovertarget and popovertargetaction, except that it (naturally) deviates to allow controlling modal vs. non-modal dialogs.

This (declarative invocation) is one of the "nice" features of popover that could naturally be brought to <dialog>. The other is "light dismiss", and for that I've opened #9373.

@scottaohara
Copy link
Collaborator

similar +1 but with one caveat, as I don't understand the use case for the toggleModal value. Unless I'm missing something, I wouldn't think that'd be possible to do, per the way a modal dialog works / would make the invoking element (which would have to be outside of the dialog) inert.

pending clarity on the above, the values could be simplified down to: show, hide, toggle, showModal

but curious to any use case i'm not thinking of to help me adjust my thinking

@keithamus
Copy link
Contributor Author

I can't think of a particularly compelling use case, other than slots which can reflect elements in or outside of the dialog depending on the open state, and so would benefit from being able to define toggleModal.

@scottaohara
Copy link
Collaborator

scottaohara commented Jun 2, 2023

so would the idea there be that a button outside of the modal dialog opens the dialog, and then that same button is re-slotted into the dialog itself so that it could toggle it closed? and somehow that would be preferable to just having a close button within the dialog?

@mfreed7
Copy link
Collaborator

mfreed7 commented Jun 2, 2023

similar +1 but with one caveat, as I don't understand the use case for the toggleModal value. Unless I'm missing something, I wouldn't think that'd be possible to do, per the way a modal dialog works / would make the invoking element (which would have to be outside of the dialog) inert.

pending clarity on the above, the values could be simplified down to: show, hide, toggle, showModal

I like this simplification; I think you're right that toggleModal and hideModal don't have much use.

I can't think of a particularly compelling use case, other than slots which can reflect elements in or outside of the dialog depending on the open state, and so would benefit from being able to define toggleModal.

Perhaps I'm missing a way to do this declaratively, but I believe to be able to do that, you'd either have to change a slot attribute or use imperative slotting. Either way, you're using JS, so I'm not sure how much value there is in supporting declarative attributes for this behavior, right?

@keithamus
Copy link
Contributor Author

keithamus commented Jun 2, 2023

Seems sensible to me.

Given other discussions around deprecating Dialog show (now that we have <dialog popover> which is, dare I say, effectively the same?) - is it worth thinking about what this would look like to align to that. Perhaps there's only a strong case for the following:

interface mixin DialogInvokerElement {
  [CEReactions] attribute Element? dialogModalTargetElement;
};

Where if this value points to a dialog, then clicking on this element would open the dialog as modal.

@Link2Twenty
Copy link

now that we have \u003Cdialog popover> which is, dare I say, effectively the same?

I think "dialog popover" is elevated to top-layer whereas a standard "dialog open" is not.

@mfreed7
Copy link
Collaborator

mfreed7 commented Jun 2, 2023

Seems sensible to me.

Given other discussions around deprecating Dialog show (now that we have <dialog popover> which is, dare I say, effectively the same?) - is it worth thinking about what this would look like to align to that. Perhaps there's only a strong case for the following:

interface mixin DialogInvokerElement {
  [CEReactions] attribute Element? dialogModalTargetElement;
};

Where if this value points to a dialog, then clicking on this element would open the dialog as modal.

Hmm very interesting point. I'm not sure everyone is 100% onboard with deprecating non-modal dialog behavior (i.e. show()) but I certainly am. I think <dialog popover> is everything a non-modal dialog should be, and more.

So I'm supportive of just <button dialogmodaltarget=foo>. I'm also supportive of the "old" proposal with dialogtargetaction etc.

I think "dialog popover" is elevated to top-layer whereas a standard "dialog open" is not.

Right. But that's almost always what you want, I think. Said another way, now that you have <dialog popover> I'm not sure why you'd ever reach for <dialog open> for a non-modal dialog.

@scottaohara
Copy link
Collaborator

scottaohara commented Jun 2, 2023

i also like this further simplification.

My primary opposition is squarely in the scope of deprecating "non-modal" dialogs. We still need those.

However, deprecating .show() and telling people to do either <dialog popover> or <dialog popover=manual> - where i would expect the latter to essentially cover for whatever we'd 'lose' with .show() - would seem a fair trade-off to me.

@mfreed7
Copy link
Collaborator

mfreed7 commented Jun 2, 2023

i also like this further simplification.

My primary opposition is squarely in the scope of deprecating "non-modal" dialogs. We still need those.

However, deprecating .show() and telling people to do either <dialog popover> or <dialog popover=manual> - where i would expect the latter to essentially cover for whatever we'd 'lose' with .show() - would seem a fair trade-off to me.

Thanks. This motivated me to finally file #9376. I agree with you.

@keithamus
Copy link
Contributor Author

I've made an attempt at a PR: #9456. An open question in the PR which I'm bringing here:

There's one large piece here that remains somewhat undefined, which is what does this do?

<button dialogmodaltarget="foo" popovertarget="foo">Open</button>
<dialog id="foo" popover>

I see there being a few options:

  • Add a check in dialog modal target element to see if the element has a popover attribute, and return null if it does.
  • Add a check in popover target element to see if the invoker has a dialogmodaltarget attribute, and return null if it does.
  • Add a check in dialog modal target attribute activation behavior to check if the invoker has a popovertarget attribute, and return early if it does.
  • Add a check in popover target attribute activation behavior to check if the invoker has a dialogmodaltarget attribute, and return early if it does.

All options essentially prefer one over the other, i.e. if a button has dialogmodaltarget then popovertarget is invalid, or vice versa. Another option is to make both invalid but this seems like quite a surprising behaviour IMO.

@scottaohara
Copy link
Collaborator

agreed, it'd be odd to have each cancel the other out so that nothing happens.

I'd personally lean towards the dialog becoming a non-modal popover if someone were to erroneously add both attribute sets.

Just seems like the situation that would result in the least potential to making content that was supposed to be accessible, inaccessible, and remove any possibility of someone becoming semi-trapped in a modal that wasn't supposed to be one.

@keithamus
Copy link
Contributor Author

keithamus commented Jun 26, 2023

Just seems like the situation that would result in the least potential...

Excellent point, I've updated the spec to go in that direction unless more compelling alternatives present themselves.

@keithamus
Copy link
Contributor Author

For those following along, the new proposal is to add invokertarget which will allow for opening of <dialog> or popover and in a more extensible manner.

Please follow #9625.

@lukewarlow
Copy link
Member

lukewarlow commented Oct 6, 2023

One capability missing from the #9625 (brilliant proposal btw) is the ability to open a dialog as a modal by default

<dialog open> works for non-modals but there's no equivalent for modal dialogs.

<dialog modal open> would be a nice way to achieve this.

My use case is that I sometimes have modal dialogs that are linked to a client side router where the URL controls their visibility. I currently call .showModal() on component mount. But if I could instead just do this declaratively that would remove the requirement for client JS execution (think SSR scenarios)

My thoughts on what <dialog modal> would do when called with JS.

show() - Throw an exception.

showModal() - Work as expected.

@keithamus
Copy link
Contributor Author

<dialog modal> was discussed further up in this (very long and so easy to miss) thread: #3567 (comment)

@lukewarlow
Copy link
Member

The open tag would still be required to make it actually modal so I don't think CSS could break it?

I would suggest a single modalopen but that would mess with lots of existing styling and also not match how modal dialogue are currently.

@keithamus
Copy link
Contributor Author

There's also discussion in #9376 about removing .show() as <dialog popover> is likely preferred for the additional benefits; top-layer, light dismiss, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accessibility Affects accessibility addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest topic: dialog The <dialog> element. topic: forms
Development

Successfully merging a pull request may close this issue.