DEV Community

Cover image for Comparing the Popover API and the <dialog> element
Matt Angelosanto for LogRocket

Posted on • Originally published at blog.logrocket.com

Comparing the Popover API and the <dialog> element

Written by Rahul Chhodde✏️

One of the most recent additions to web standards is the Popover API, which appears somewhat similar to the HTMLDialogElement API. In certain instances, the distinction between these two can be rather unclear, making it confusing for developers to choose the right one for a given task.

This article aims to address the key similarities and differences between the Popover and HTMLDialogElement APIs. We will briefly describe each API, cover some of their different use cases, implement the APIs to create some common utilities seen on the web, and discuss when to use each API.

Jump ahead:

Understanding how dialogs work

A dialog UI pattern can serve as a means to display information to users and gather their input. "Dialog" here describes the communication process where the app requests user input using this particular element. The user may need to respond before proceeding with the app, or they can choose to dismiss the dialog.

To craft dialogs that are semantically rich and accessible for the web, the HTML standard supports the HTMLDialogElement API. This API enables the creation of purposeful dialogs with improved accessibility.

The <dialog> element provided by the API is specifically designed for components that require user interaction or input, and it can operate in both modal and non-modal manners.

Exploring modal dialogs

In a modal dialog, users need to handle the dialog first before they can continue using the app. This means they can't do anything else in the app until they close or cancel the dialog.

Modal dialogs are usually used for important tasks like confirming actions or showing errors, which need the user's complete focus.

A common example of modal dialogs is a newsletter subscription prompt. The modal behavior is particularly useful in this case for grabbing the user's full attention, as shown in this example from Dribbble: Example Of Modal Dialogs Showing Newsletter Subscription Prompt For Braun Wristwatches Displayed Over Demo Site With Text Input For Email, Button To Subscribe, And Button To Close Modal

Another excellent example, though less common, is when a website presents a dialog asking the user for permission to show ads. Here’s an example from the How-To Geek website: Example Of Modal Dialogs From How To Geek Website Asking User For Permission To Show Ads With Button To Allow Ads While Using Site And No Other Option To Close Modal Or Use Site Until Permission Is Granted

In this scenario, the user's access to the app or website is restricted until they include it in their ad blocker program's whitelist.

Exploring non-modal dialogs

Non-modal dialogs allow users to keep using the app even when the dialog is open. This makes them less bothersome for users, but it also means they might not immediately notice the dialog.

They’re typically used for features like tooltips or menus that don't need all of the user's focus.

Interestingly, dialogs with non-modal behavior are frequently categorized as popovers. This is where the distinction between dialogs and popovers starts to blur, making it difficult for developers to choose between the two.

For a clearer perspective, you may think of the dialog vs. popover debate as modals vs. non-modals.

A frequent situation where non-modal dialogs come into play is with the cookie-consent banners often seen on websites and apps these days: Example Of Non Modal Dialogs From Awwwards Website. Red Box With Text And Arrow Pointing To Non Modal Dialog Describes Cookie Consent Box Created With Non Modal Popover Informing User About Cookie Use On Site With Link To Policy. User Is Not Restricted From Using Site While Non Modal Dialog Is Displayed

The recommended way to create and control dialogs in HTML is to use the <dialog> element in conjunction with its HTMLDialogElement API. This approach gives the element an inherent dialog role, which allows the browser to accurately interpret the element and provide appropriate accessibility features.

In the upcoming segment, we will go over the specifics of the dialog API and see how to use it to create comprehensive and functional dialogs.

Diving into the HTMLDialogElement API

Constructing a functional HTML dialog requires a solid understanding of how to structure the markup correctly and utilize the HTMLDialogElement API for various dialog-related tasks.

Furthermore, there are certain new CSS features available that can be employed to style, optimize, and enhance accessibility for HTML dialogs.

We will address the process of declaring a dialog through markup, adding functionality to it using JavaScript, and enhancing its appearance with CSS, one step at a time.

Structuring your markup for dialogs

When you add the <dialog> element to your HTML, it will not appear immediately on the frontend as a proper dialog. To make it visible, you can add the open attribute to it:

<dialog id="dialog" open>
  <p>...</p>
</dialog>
Enter fullscreen mode Exit fullscreen mode

The result, which you can see in the CodePen here, doesn’t quite look like a dialog at the moment. It currently operates as a non-modal dialog and also lacks a close button.

To enhance the functionality of this dialog, you can employ the HTMLDialogElement API to programmatically manage its display, closure, and interaction using JavaScript.

Leveraging JavaScript with the HTMLDialogElement API

We can create both modal and non-modal dialogs using the JavaScript capabilities of the HTMLDialogElement API. The JavaScript support within this API offers various methods, events, and properties to effectively handle the behaviors and functionalities of the dialog element.

Here are some JavaScript methods supported by the API to manage different states of the dialog element:

  • show(): Opens the dialog non-modally
  • showModal(): Opens the dialog modally
  • close(): Closes the dialog and optionally returns a value to the caller

It also supports specific events that you can listen to whenever the dialog closes:

  • The close event fires when the dialog closes
  • The cancel event fires when the user cancels the dialog by pressing the Escape key

Some instance properties are also available with the API, which are listed below:

  • open: A boolean value that indicates whether the dialog is open (true) or closed (false)
  • returnValue: A property that can be used to set or get the return value for the dialog element

Styling your dialog with CSS pseudo-selectors

A modal dialog is supposed to perform critical tasks and therefore appears on the top layer of the stacking context, which means you can't manage its stacking using the CSS z-index property. Instead, it will always appear on top of every element that is not in the top layer.

Note that the stacking of the elements in the top layer is managed in last-in, first-out (LIFO) order. This means that the last element that is added to the top layer will be the topmost element.

On the other hand, non-modal dialogs let you control stacking with CSS. The comparison is shown in the CodePen example here.

With the HTMLDialogElement API, we have access to dialog-specific CSS pseudo-selectors, which can be used to apply styles or target specific dialog components.

When a dialog behaves modally, it should display a full-screen click trap in the background. This indicates that the rest of the application is currently inactive.

The ::backdrop pseudo-element, which is also used in user-agent styles to provide a pre-existing default click trap for modal dialogs, can be used to customize the click trap appearance:

.my-modal::backdrop {
  background: hsl(240deg 50% 50% / 25%);
}
Enter fullscreen mode Exit fullscreen mode

We can also use the :modal CSS class to ensure that the dialog we are targeting exhibits modal behavior and style it accordingly:

.my-dialog:modal {
  ...
}
Enter fullscreen mode Exit fullscreen mode

Implementing the HTMLDialogElement API

You can start implementing the HTMLDialogElement API by getting a reference to a button element that triggers the dialog. When the user interacts with the button, you can execute the Dialog.show() method. This will cause the dialog to show up when the button is clicked:

const openDialogBtn = document.querySelector("#open-dialog-btn");
const dialog = document.querySelector("#dialog");

openDialogBtn?.addEventListener("click", () => {
  dialog?.show();
});
Enter fullscreen mode Exit fullscreen mode

At this point, the dialog is non-modal, which means that the user can still interact with the rest of the application while the dialog is open.

If you want to make the dialog modal, you can use the Dialog.showModal() method instead of Dialog.show(). This will cause the dialog to operate in a modal behavior, which means that the user will not be able to interact with the rest of the application until the dialog is closed:

openDialogBtn?.addEventListener("click", () => {
  dialog?.showModal();
});
Enter fullscreen mode Exit fullscreen mode

Closing dialog elements can be readily achieved by utilizing Dialog.close(), as shown below. This method constitutes an explicit dismissal:

closeDialogBtn?.addEventListener("click", () => {
  dialog?.close();
});
Enter fullscreen mode Exit fullscreen mode

Soft dismissal of dialogs is not natively supported by the HTMLDialogElement API. The Popover API provides a superior way to create non-modal dialogs with soft dismissal capabilities.

Both triggering and dismissing, whether softly or explicitly, are much simpler using the Popover API than the dialog API. We will discuss this topic further in the next section.

Here is a CodePen demonstration of both modal and non-modal dialogs, implemented based on the explanations given earlier.

The modal dialog is neatly centered thanks to the applied user agent styles. The non-modal counterpart, on the other hand, does not quite achieve centered alignment and is visible around its anchor or trigger.

Feel free to fork this code, try to fix the non-modal positioning, or create your own variations of dialog elements.

As we previously discussed, dialog elements are suitable for presenting users with important action-related information. However, for less crucial scenarios, we now have the newly introduced Popover API. Let’s explore it in the next section.

Understanding how popovers work

A popover can be any transient window-like component that emerges when triggered within a webpage or app. Usually, this happens when a user interacts with an element that serves as the trigger for the popover. It is typically used to provide additional information or functionality to the user.

The Popover API provides the advantage of utilizing in-browser features to create accessible and native popover utilities. We will discuss more about the API later on.

Popovers exhibit non-modal behavior and are not suitable for elements displaying modal properties. They are frequently used for tasks such as:

  • Displaying tooltips or help text
  • Opening action or context menus featuring a range of choices
  • Initiating non-modal dialogs for actions like toast notifications, hover cards, fly-outs, etc.
  • Providing context-related information during application tours or onboarding processes

Tooltips that appear when hovering over buttons or icons, or are added when a new feature is introduced, are good examples of non-modal popovers. These unobtrusive popovers provide quick insights without disrupting the user's flow, like this Unsplash popover shown below: Example Non Modal Popover From Unsplash Website. Rounded White Box With Red Dotted Outline And Red Arrow Pointing To Non Modal Popover Describes A Simple Tooltip Introducing A New Unsplash Feature With Button To Dismiss Popover. User Is Not Restricted From Using Site While Popover Is Active Another practical use case of popovers is providing context-specific information. For instance, when a user clicks on a term or phrase within an article, a popover might appear, offering a brief definition or explanation.

These popovers are designed to enhance the user's understanding without taking them away from the main content, like this example from the Grammarly website: Example Of A Non Modal Popover From Grammarly Website. White Rounded Box With Red Dotted Outline And Red Arrow Pointing To Non Modal Popover Describes Popover Purpose Of Showing Suggestions To User On Hover. User Is Not Restricted From Using Site While Popover Is Active Now, let’s delve further into the Popover API, exploring its capabilities and functionalities in more detail.

Exploring the Popover API

To handle non-modal behavior beyond dialogs in web browsers, the HTML standard has introduced popover attributes with the corresponding Popover API.

Unlike the HTMLDialogElement API, the Popover API doesn't involve creating a new element tag in the markup. Instead, it utilizes different attributes that can be added to various elements. These attributes establish a relationship between the popover and its triggering element.

The HTMLDialogElement API is specifically linked to dialogs. In contrast, the Popover API can be attached to any element to provide it with a non-modal behavior.

This new approach may eliminate the need for an external library in the future to achieve non-modal behavior. It also enhances the user experience by incorporating native semantics and providing relevant information in a non-modal manner.

By default, popovers created using this approach have a light-dismissing behavior that can also be controlled through API features.

As mentioned before, popovers exhibit non-modal behavior. Popovers generated using the Popover API strictly adhere to this non-modal behavior and are placed at the top-most layer in the stacking context. This implies that they will consistently appear above other elements, irrespective of their z-index property.

Using the popover attribute

The popover attribute is a global attribute that designates an element as a popover element. It can take one of two values: auto or manual.

The default value of this attribute is auto, which indicates that the popover will automatically display when the user interacts with the triggering element.

The manual value indicates that the popover will only be shown when the user explicitly clicks on the triggering component.

Using the popovertarget attribute

The popovertarget attribute is used to specify which element will trigger the popover. The ID of the trigger element must be in the same tree as the element with the popover attribute. The popover will be displayed relative to the triggering element, as you can see in the CodePen example here.

Using the popovertargetaction attribute

The popovertargetaction attribute is used to specify the action that will be performed when the popover is triggered. It can take one of three values:

  • show — Interacting with the triggering element will display the popover
  • hide — Interacting with the triggering element will hide the popover
  • toggle — Interacting with the triggering element will display the popover if it is hidden and hide it if it is displayed

Below, you can interact with two triggering elements. The Show popover button uses the show value, while the Hide popover button uses the hide value. See the CodePen example here.

Exploring JavaScript support in the Popover API

In terms of JavaScript support, there are three instance properties that prove useful for acquiring the attribute values discussed earlier:

  • popover — Facilitates retrieving and setting an element's popover state using JavaScript, which can be either auto or manual. It serves not only for functional purposes but also for feature detection, reflecting the value of the popover global HTML attribute
  • popoverTargetElement — Enable getting and setting the popover element controlled by the corresponding control triggers. They correspond to the JavaScript counterparts of the popovertarget HTML attribute
  • popoverTargetAction — Employed to obtain and modify the action to be executed (hide, show, or toggle) on the popover element under the control of the respective button. They mirror the value of the popovertargetaction HTML attribute

In addition, the API supports three instance methods:

  • hidePopover() — Conceals a popover element by removing it from the top layer and applying the display: none style
  • showPopover() — Reveals a popover element by adding it to the top layer
  • togglePopover() — Toggles a popover element between its visible and hidden states

Furthermore, two instance events are available for use with the API:

  • beforetoggle — Triggered just prior to a popover element's state transition between showing and being hidden, or vice versa
  • toggle — Fired just after a popover element's state changes between showing and being hidden, or vice versa

The popover-specific CSS

Popover elements — specifically, those that use the popover attribute — are intended to be displayed above other elements. Thus, like modal dialog elements, they are placed in the top layer of the stacking context.

Since we've previously addressed the finer points of stacking with top-layer elements, there's no need to reiterate them here.

With the Popover API, we have the :popover-open CSS pseudo-class that enables you to target a popover element when it's in an open state:

.tooltip:popover-open {
  /* Do something */
}
Enter fullscreen mode Exit fullscreen mode

As popover elements reside at the topmost level of the stacking context, they also support the ::backdrop pseudo-element. This feature can be highly advantageous for emulating effects such as lightbox image galleries, introduction tooltips during app tours, walkthroughs, onboarding, and more:

.lightbox::backdrop {
  /* Do something */
}
Enter fullscreen mode Exit fullscreen mode

Implementing the Popover API

The Popover API imparts popover behavior through its attributes. However, unlike the HTMLDialogElement API, it does not inherently include the associated semantics. Thus, it becomes your responsibility to incorporate semantics into your popovers by utilizing distinct role attributes.

For example, when creating a custom <select> list box, you would include the role attribute with the value listbox to indicate that the element functions as a list box. You should also apply appropriate ARIA states and properties, which will signal the browser to identify it as such:

<div 
  role="listbox" 
  aria-labelledby="..."
  aria-activedescendant="..."
  popover>
  ...
</div>
Enter fullscreen mode Exit fullscreen mode

Alternatively, if an element possesses sufficient inherent semantics, you can simply add the popover attribute. Let's take the <dialog> element as an example to illustrate how easily we can trigger and dismiss it using the Popover API.

We can easily establish a functional dialog using the aforementioned popover attributes, as discussed earlier, to mimic a cookie-consent box:

<dialog id="cookie-box" popover="manual">
   ...
   <button 
     id="accept-cookies" 
     popovertarget="cookie-box" 
     popovertargetaction="hide">Accept Cookies</button>

   <button
     id="close-cookie-box"
     popovertarget="cookie-box" 
     popovertargetaction="hide">Close</button>
</dialog>

<button 
  id="open-cookie-box"
  popovertarget="cookie-box" 
  popovertargetaction="show">Open Cookie box</button>
Enter fullscreen mode Exit fullscreen mode

After tweaking the position and appearance of the dialog element with CSS, we are good to add more functionality to our cookie-consent box. Check how flawlessly the trigger and dismissal of the dialog popover work in the demo below. See the CodePen here.

Typically, websites display the cookie-consent box in an initially opened state. You can achieve this in JavaScript by obtaining a reference to the dialog box and subsequently utilizing the Popover.showPopover() method:

const cookieBox = document.querySelector("#cookie-box");
cookieBox.showPopover();
Enter fullscreen mode Exit fullscreen mode

You may now take it a step further by defining actions and setting up event listeners to incorporate additional functionalities into the cookie-consent box. Here's how the final demonstration looks.

As an assignment, consider expanding on this example to create a comprehensive cookie-consent feature.

With your current understanding of dialogs and popovers, you may venture into developing something new too. Some ideas include a basic image lightbox, a simple tooltip, or any other idea that comes to mind when considering a popover-like interaction.

When to use dialogs vs. popovers

You now possess a sufficient technical understanding of dialogs and popovers. However, there are a few additional non-technical aspects you should consider when choosing between dialogs and popovers. Take the following factors into account:

  • User context: If a user is already engaged in a complex task, a dialog may cause less disruption than a popover. However, if the user is starting a task, a popover may be a better way to provide additional information without interrupting their flow
  • User experience: If the user is acquainted with the task or interaction, a popover might suffice. Conversely, if the user is unfamiliar with the task or interaction, a dialog could be a superior method for delivering lucid instructions and gathering essential input
  • Platform: Certain platforms, such as mobile devices, are more sensitive to modal windows. In such cases, opting for a popover over a dialog might be a better idea

Keeping these in mind can help you determine whether a dialog or popover is best for your needs.

Checking browser support for the Popover and HTMLDialogElement APIs

The Popover API is presently in an experimental phase across most browsers, except for Chrome 116+ and the Safari 17 technical preview. However, it is expected to be available in most major browsers soon. You can refer to CanIUse for more details about its support.

The HTMLDialogElement API is now well-supported and available in nearly all modern web browsers. You can find additional information about support for <dialog> elements on CanIUse.

Conclusion

The difference between dialogs and popovers can be subtle and challenging to discern. In this guide, we talked about both their differences and similarities. For example, non-modal dialogs can work like popovers, and the Popover API can be used flexibly with <dialog> elements.

We also explored some UI patterns that are similar to these two concepts and completed some coding exercises. As the popover feature is still in its early stages, it may continue to evolve, potentially making the distinction between these two APIs clearer.

I hope you found the explanations helpful. Feel free to share any thoughts, suggestions, or ideas you might have.


Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.

npm:

$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
Enter fullscreen mode Exit fullscreen mode

Script tag:

Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Enter fullscreen mode Exit fullscreen mode

3.(Optional) Install plugins for deeper integrations with your stack:

  • Redux middleware
  • ngrx middleware
  • Vuex plugin

Get started now

Top comments (1)

Collapse
 
cezarytomczyk profile image
Cezary Tomczyk

Worth mentioning that popovertarget isn't that well supported yet :-(