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

[Popup] Why does it require JavaScript? #437

Closed
erikkroes opened this issue Jan 22, 2021 · 19 comments
Closed

[Popup] Why does it require JavaScript? #437

erikkroes opened this issue Jan 22, 2021 · 19 comments
Assignees

Comments

@erikkroes
Copy link

As asked by Steve Faulkners on Twitter: https://twitter.com/stevefaulkner/status/1352326448397348880

It was suggested to create an issue that people can "+1". Didn't see one, so here it is!

I think it's strange for an element to require JS while it could work without. It is strange for a specification in one language to require another. I should be able to build HTML without JS if I wish to.

@thepassle
Copy link

Related to this, I suggested something on twitter yesterday that could potentially do something here.

pseudocode:

<button id="menubutton">open</button>
<popup invoker="menubutton">
  <!-- Menu content -->
</popup>

Jonathan Neal also suggested the name controller.

Not sure really how feasible this would be, but I figured I'd give my 2c, and twitter conversations tend to get buried 😄

Additionally, regarding the JS api, it may be interesting to allow the following API:

popup.show({
  delegatefocus: true|false,
  autofocus: true|false
});

@claviska
Copy link

I'd like to suggest exposing an open attribute in lieu of (or possibly in addition to) the show() and hide() methods, keeping its usage more in line with existing elements such as <dialog>.

Allowing this functionality without JavaScript is an even more eager goal. My concern is not having control over the show/hide transitions via CSS.

Until show is called, the popup does not display (has a computed value of none for its display property).

If we use a method or attribute to show the popup, we can work around the display: none problem. How would we do that if we used an invoker attribute or similar? The use case is subtle fade ins/outs, for example.

This may seem like a silly thing to worry about, but users have come to expect this level of customization from dialogs, popups, et al so not enabling it will lead to reluctance in adoption.

@thepassle
Copy link

This may seem like a silly thing to worry about

I dont think it's silly at all 🙂 I agree that CSS transitions definitely should be considered.

I also think exposing an open is a good idea, but I think itd be a shame if the open attribute would only be available for an initial 'render'; having an open attribute doesnt really allow for the element to be interactive without javascript, so something would be still needed there.

Consider details/summary as an example, they also have an open attribute, but are still able to be interactive without javascript. I feel like thats important for popup also.

@Malvoz
Copy link
Contributor

Malvoz commented Jan 23, 2021

pseudocode:

<button id="menubutton">open</button>
<popup invoker="menubutton">
  <!-- Menu content -->
</popup>

If instead the button had an invoke="{id-of-popup}", we could re-use and reference that ID with aria-controls for backwards compatibility (and not always need an ID on both components, unless anchored positioning is needed):

<button invoke="menu" aria-controls="menu">open</button>
<popup id="menu">
  <!-- Menu content -->
</popup>

@aardrian
Copy link

Per @stevefaulkner's original question (and ignoring the other comments here addressing CSS and naming), as I read through the explainer I also struggled to understand why script is necessary.

I am motivated to not rely on script given how often I see scripts fail to load or run on client engagements, which seems to disproportionately impact users with lower-end devices, old devices, poor connections, disabilities, and locked-down environments.

With that framing in mind...

This proposal feels closer to a disclosure widget than a dialog. A dialog need not always have a trigger element, but a disclosure must. The <popup> proposal suggests a trigger element (except the "teaching bubble" example, which is a dialog) and I think that is a critical differentiator.

Considering that, we already have a native HTML disclosure widget in <details> & <summary>. The key differences between <details>/ <summary> and <popup> (at a Saturday morning read of the explainer) appear to be:

  • <popup> comes with default CSS (z-index & language-dependent absolute positioning);
  • <popup> is dismissable;
  • <popup> allows for only one expanded at a time;
  • <popup> has no built-in trigger element.

In my head I see the following, leaning on the existing for attribute and <summary>'s open attribute:

<button for="Foo" open>
<popup id="Foo">
  Hey there.
</popup>

Even though I am abusing for, it leans on an existing HTML pattern, does not require folding ARIA into the HTML, and helps ensure a (keyboard) functional trigger exists.

@noamr
Copy link

noamr commented Jan 24, 2021

I believe javascript is necessary and is an important piece of the spec, otherwise it creates a breach to CSS stacking contexts for embedded HTML/CSS. I'll try to explain.

There are many times a stacking context is a type of guarantee that a piece of embedded HTML/CSS will not occlude an unrelated part of the DOM. For example, in wikipedia, the text of the wiki (which is embedded HTML/CSS) is not allowed to occlude the navigation elements, and it's enforced by having its parent create a stacking context.
If popups were openable without JavaScript, a piece of embedded content could contain a popup which hides navigation bars, ads, and other things outside the scope of embedding.

By enforcing use of JS, the embedder decides when to open the popup rather than letting the content make that decision.

@aardrian
Copy link

I fail to understand how enforcing JavaScript-only triggers on an HTML element is the only way to address its CSS stacking context.

I also don't understand your Wikipedia example. Wikipedia is styled content, not raw HTML. Without seeing a sample URL or the HTML structure you have in mind, I am not sure what you are citing (let alone if you mean the desktop or mobile version of Wikipedia).

For <select>, the displayed <option>s occlude unrelated content and do not require JavaScript for display. Granted, <option> has a dedicated trigger, which this proposal lacks and which I propose above.

@noamr
Copy link

noamr commented Jan 25, 2021

I fail to understand how enforcing JavaScript-only triggers on an HTML element is the only way to address its CSS stacking context.

It's not the only way, but it's the most obvious way.

I also don't understand your Wikipedia example. Wikipedia is styled content, not raw HTML. Without seeing a sample URL or the HTML structure you have in mind, I am not sure what you are citing (let alone if you mean the desktop or mobile version of Wikipedia).

The "Wiki text", for example of a wikipedia article, is styled content - but the embedding page does not let it run its own Javascript and keeps it within its own stacking context, so that it doesn't occlude banners and navigation elements.
It's currently not possible for the wiki text to break that barrier without Javascript.

It's desktop only, here are some materials:
https://www.mediawiki.org/wiki/Extension:Popups
https://techblog.wikimedia.org/2020/11/23/web-performance-case-study-wikipedia-page-previews/

Note that a lot of this was discussed in an existing github issue about the subject:
whatwg/html#4633 (comment)

For <select>, the displayed <option>s occlude unrelated content and do not require JavaScript for display. Granted, <option> has a dedicated trigger, which this proposal lacks and which I propose above.

Dedicated triggers are a good alternative to "Javascript only" IMO. My point is about "Javascript only" vs allowing popups to be visible with ambient CSS (without interactions).

@aardrian
Copy link

@noamr

The "Wiki text", for example of a wikipedia article, is styled content - but the embedding page does not let it run its own Javascript and keeps it within its own stacking context, so that it doesn't occlude banners and navigation elements.

Thank you for the context and links.

This proposal should not replicate what Wikipedia has done. Those examples are not exposed in the accessibility APIs. For example, when I navigate the Sloth page (shown as an example in one of those links) and use the virtual cursor of my screen reader, I hear the links but no visible overlays are shown. If I tab my way through each link, then the overlays appear but they are not exposed to my screen reader in any way. I cannot navigate into them.

I appreciate you want to use these as an analogue, but these should not be it.

Do you have a native HTML analogue that is accessible?

I think @smhigley provided a good example of how the stacking context matters when considering a trigger:

I don't currently know how to have a child popup that breaks out of a container w/ overflow: hidden/scroll, or a parent w/ z-index (e.g. https://jsfiddle.net/98pvLgsw/show)

Do you have an approach to creating a reusable popup that works in those cases w/o fixing it or putting it in <body>?

https://twitter.com/codingchaos/status/1353514104049209344

Is this what you meant?

@noamr
Copy link

noamr commented Jan 25, 2021

@noamr

The "Wiki text", for example of a wikipedia article, is styled content - but the embedding page does not let it run its own Javascript and keeps it within its own stacking context, so that it doesn't occlude banners and navigation elements.

Thank you for the context and links.

This proposal should not replicate what Wikipedia has done. Those examples are not exposed in the accessibility APIs. For example, when I navigate the Sloth page (shown as an example in one of those links) and use the virtual cursor of my screen reader, I hear the links but no visible overlays are shown. If I tab my way through each link, then the overlays appear but they are not exposed to my screen reader in any way. I cannot navigate into them.

I appreciate you want to use these as an analogue, but these should not be it.

Do you have a native HTML analogue that is accessible?

The wikipedia popups have aria-hidden on them on purpose, the information in the popup is a "preview", and is an augmentation rather than necessary content. I wasn't proposing that HTML popups should replicate the same thing, but rather using that as an example for "embedded content" vs. "embedding UI" and some challenges it presents.

I think @smhigley provided a good example of how the stacking context matters when considering a trigger:

I don't currently know how to have a child popup that breaks out of a container w/ overflow: hidden/scroll, or a parent w/ z-index (e.g. https://jsfiddle.net/98pvLgsw/show)
Do you have an approach to creating a reusable popup that works in those cases w/o fixing it or putting it in <body>?

https://twitter.com/codingchaos/status/1353514104049209344

Is this what you meant?

Yes, something like that. Right now, you have to put it as a child of body in order to break out of overflow/stacking. It's not clear to me whether the popup proposal does the same thing by default, or relies on the new to-be-defined anchor positioning. The proposal hints about that but doesn't say so explicitly. If the new popups don't break existing z-index/overflow, than this reason for requiring JS is moot.

@claviska
Copy link

Thinking more about this, another concern I have is that an HTML-only "invoker" introduces a new pattern we don't currently have in HTML. Even <dialog> has an open attribute and a showModal() method thats requires JS to toggle dynamically.

My knee-jerk reaction was "if we do this, can we at least make all existing components work the same way?" But then more I think about it, the less that makes sense.

With <dialog>, for example, we can show it initially with <dialog open>, but how does the dialog know when it should be dismissed? And will we ever really have it open initially? I think we'll be more apt to open it after some sort of user action, likely with JavaScript.

Popups, as proposed herein, don't have the latter problem because of the "light dismiss" behavior, but must they always be invoked with a click? A tooltip might show on hover/focus, a popover might show on hover, a select-style menu might show on focus.

Should the proposed invoke attribute handle granular customizations like this? My gut instinct is no, it shouldn't, because once we go down this path we're conflating HTML "behaviors" with what we can already do in JavaScript. To me, that just makes HTML more complex than it needs to be.

IMO, we should keep this consistent with how dialog works:

<popup>Closed initially</popup>
<popup open>Open initially</popup>

This lets us display the element in both states with pure HTML, but toggling that state dynamically would, intuitively, require JavaScript. To change existing behavior or introduce new patterns will confuse users and, IMO, is something we should discuss in a separate RFC.

@thepassle
Copy link

With , for example, we can show it initially with , but how does the dialog know when it should be dismissed? And will we ever really have it open initially? I think we'll be more apt to open it after some sort of user action, likely with JavaScript.

As a counter argument to this, details/summary also have an open attribute, yet are still able to function interactively without JS. The fact that dialog requires JS for interactivity seems more like a lacking feature to me.

Also, if the "light dismiss" behavior(/interactivity) is able to be implemented natively, why wouldnt there be a native way to open it also? That seems really off to me.

@claviska
Copy link

Summary is always contained within details, so the trigger is colocated and therefore inferred. Dialogs and popups are independent of their triggers, so that makes it trickier, hence the proposed invoker pattern.

But you raise a good point. Although I would prefer to handle this with JavaScript, at the end of the day, I'll be happy as long as dialog, popup, and whatever future elements have a consistent API. 😄

@aardrian
Copy link

@noamr

If the new popups don't break existing z-index/overflow, than this reason for requiring JS is moot.

Agreed, though I think this proposal needs more clarity on that point.

@claviska

And will we ever really have it [dialog] open initially? I think we'll be more apt to open it after some sort of user action, likely with JavaScript.

I have worked on projects where the dialog is launched at page load. Time-out alerts, errors, etc. Agreed less common, but still a use case in the wild.

This [an open attribute] lets us display the element in both states with pure HTML, but toggling that state dynamically would, intuitively, require JavaScript.

Unless it is its own triggering element, I think I agree? At least with <details>/<summary> it has an embedded trigger.

@thepassle

The fact that dialog requires JS for interactivity seems more like a lacking feature to me.

Same, though there are still cases where no trigger would exist (time-out alerts, error on page load).

I think this conversation is valuable to help sort the use cases in my own head, but hopefully others' as well.

@Malvoz
Copy link
Contributor

Malvoz commented Jan 25, 2021

Btw there's an open issue for opening dialogs without scripting: whatwg/html#3567.

@aardrian
Copy link

aardrian commented Jan 25, 2021

@Malvoz Sadly, the most recent activity by the WHATWG folks on that issue was to propose removing <dialog> completely owing to lack of implementation. Whether that lack of implementation is a function of a poor spec, poor use case, or maybe even a broad failure to implement inert is up for some debate.

@melanierichards
Copy link
Contributor

Hi everyone, catching up to this weekend's discussion!

I mentioned to Steve on Twitter that the intent for the final API is to include some declarative method for popup invocation. We placed that under "areas for future exploration" because there were some interesting questions around scope to think through, but can certainly understand how that might have implied lower priority. Our initial design for this was an attribute that would tie "invoker" to "invokee", and we wanted to get a little bit more crisp about:

  • Exactly which elements are valid as an "invoker" (just interactive elements IMHO, but a subset of them or?)
  • Exactly which elements are valid as an "invokee" (maybe just popup for now, dialog a reasonable future candidate)
  • How authors would express which user gestures/interactions invoke the "invokee" (just click events? Other user interactions?)
  • If other user interactions are supported for invocation, should those potentially change dismissal behaviors as managed by the user agent?

We didn't feel we had a solid enough proposal there yet.

This discussion is super useful to us in a) illustrating the demand for a declarative approach, and b) gathering any feedback on the above points. So for anyone newly joining this thread, please do upvote if this is important to you!

Regarding top-layer management: we do need events in order to rationalize which popup should be displayed on the top layer (in cases where multiple would be displayed based on ancestry), but receiving an event from user interaction with the "invoker" should suffice.

@Malvoz
Copy link
Contributor

Malvoz commented Feb 10, 2021

  • Exactly which elements are valid as an "invoker" (just interactive elements IMHO, but a subset of them or?)
  • Exactly which elements are valid as an "invokee" (maybe just popup for now, dialog a reasonable future candidate)

HTML <menu> actually had an "invoker" concept at one point (see the MDN example), for <popup> I believe it'd translate to the following HTML:

<button type="popup" popup="my-popup">
  Invoker
</button>

<popup id="my-popup">
  ...
</popup>

In this case, <button type="popup"> would be the only valid invoker, and presumably (but not necessarily, I haven't dug into it) the popup attribute must point to the ID of a <popup> element.

Just an observation! 🙂

@melanierichards
Copy link
Contributor

Thanks again everyone for weighing in! The proposal has now been updated for script-less invocation via 369cd27

Closing this thread as the root feedback should be addressed, but please do feel free to file new issues on finer points as needed. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants