Popover API
Menus that drop down, little "what is this?" tips and "Saved!" messages all float above the page and disappear again. Building them used to need a lot of JavaScript and careful z-index values. Today the popover attribute does most of the work for you.
In this lesson you will create popovers that open and close from buttons without any script, learn the difference between auto and manual popovers, and control them from JavaScript when you need to.
Your first popover
Two attributes are all you need:
popoveron the element that should float. It is hidden until opened.popovertargeton a<button>, set to theidof the popover. Clicking the button opens the popover, and clicking it again closes it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Popover</title>
</head>
<body>
<h1>Check your ticket</h1>
<label for="pnr">PNR number</label>
<input id="pnr" name="pnr" inputmode="numeric">
<button popovertarget="pnr-help">What is a PNR?</button>
<div id="pnr-help" popover>
<p>The PNR is the 10-digit number printed at the top of your train ticket.</p>
</div>
</body>
</html>Open the popover, then click anywhere outside it or press Esc. It closes. This is called light dismiss: the popover goes away as soon as the user does something else, just like a native menu.
What the browser does for you
- Top layer. An open popover is drawn in a special layer above everything else on the page, so you never have to fight with
z-index. - Hidden by default. Until it is opened, the popover is not displayed at all.
- Light dismiss. Clicking outside or pressing Esc closes it.
- One at a time. Opening one popover closes any other open popover, unless one is inside the other (for example, a sub-menu inside a menu).
- Keyboard support. The trigger is a real
<button>, so it works with Tab, Enter and Space.
By default the browser shows a popover in the middle of the window with a thin border. You can style it like any other element, and you can place it next to its button with CSS Anchor Positioning.
Show, hide or toggle
By default a popovertarget button toggles its popover. The popovertargetaction attribute changes that:
popovertargetaction="toggle"opens or closes (the default).popovertargetaction="show"only opens.popovertargetaction="hide"only closes.
A "hide" button inside the popover gives touch users an obvious way to close it. The example also styles the popover with the :popover-open pseudo-class, which matches only while the popover is showing.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Language Menu</title>
<style>
#lang-menu { border: 1px solid #aaa; border-radius: 8px; padding: 12px 20px; }
#lang-menu:popover-open { box-shadow: 0 8px 24px rgb(0 0 0 / 0.2); }
</style>
</head>
<body>
<button popovertarget="lang-menu" popovertargetaction="show">Language</button>
<div id="lang-menu" popover>
<h2>Choose a language</h2>
<ul>
<li><a href="#">English</a></li>
<li><a href="#" lang="hi">हिन्दी</a></li>
<li><a href="#" lang="ta">தமிழ்</a></li>
</ul>
<button popovertarget="lang-menu" popovertargetaction="hide">Close</button>
</div>
</body>
</html>auto or manual?
The popover attribute has two main values. Writing popover on its own is the same as popover="auto".
popover="auto" | popover="manual" | |
|---|---|---|
| Light dismiss (click outside, Esc) | Yes | No |
| Closes other open popovers | Yes | No |
| Several open at once | No (except nested ones) | Yes |
| How it closes | Light dismiss, button or script | Only a button or script |
| Good for | Menus, tips, pickers | Toast messages, persistent panels |
Use auto for almost everything the user opens on purpose. Use manual when the popover must stay visible while the user keeps working, such as a "Profile saved" message that your code removes after a few seconds.
Controlling popovers with JavaScript
Every popover element has three methods: showPopover(), hidePopover() and togglePopover(). It also fires a toggle event after it opens or closes; the event's newState property is "open" or "closed".
Here a manual popover works as a toast message. It appears when the user saves and hides itself after three seconds.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Toast</title>
<style>
#toast {
inset: auto 16px 16px auto;
margin: 0;
padding: 10px 16px;
border: none;
border-radius: 6px;
background: #1b5e20;
color: white;
}
</style>
</head>
<body>
<h1>Edit profile</h1>
<p>Name: Ananya Iyer, Kochi</p>
<button id="save">Save profile</button>
<p id="log"></p>
<div id="toast" popover="manual">Profile saved</div>
<script>
const toast = document.querySelector("#toast");
const log = document.querySelector("#log");
document.querySelector("#save").addEventListener("click", () => {
if (!toast.matches(":popover-open")) {
toast.showPopover();
setTimeout(() => toast.hidePopover(), 3000);
}
});
toast.addEventListener("toggle", (event) => {
log.textContent = "Toast is now " + event.newState;
});
</script>
</body>
</html>Click outside the toast while it is showing: it stays, because manual popovers have no light dismiss. The CSS moves it to the bottom-right corner by changing inset and removing the default centring margin.
Popover or dialog?
A popover does not block the page. The user can ignore it and carry on. When the user must answer before continuing, such as confirming a payment, use a modal <dialog> instead, as shown in Details, Summary & Dialog.
Common mistakes
- Putting
popovertargeton a<div>or<a>. It only works on buttons. - A trigger button inside a form. A button inside a
<form>is a submit button by default and will not open the popover. Addtype="button". - Misspelt ids. The
popovertargetvalue must match the popover'sidexactly. - Using
manualand forgetting to close it. Manual popovers stay open until your button or code hides them. - Using a popover for a required decision. Use a modal dialog when the page must wait for the user.
Key points
- The
popoverattribute turns any element into a floating panel that starts hidden. - A
<button>withpopovertargetopens and closes it without JavaScript;popovertargetactioncan force show or hide. autopopovers (the default) light dismiss and close each other;manualpopovers stay open until closed explicitly.- Open popovers sit in the top layer, so
z-indexis not needed. - From script, use
showPopover(),hidePopover(),togglePopover()and thetoggleevent.
Exercise
Build a "Share" button for a recipe page. Clicking it opens an auto popover containing a heading and three links (Email, Copy link, Print), plus a Close button that only hides the popover. Style the open popover with :popover-open so it has rounded corners and some padding.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Share Recipe</title>
<style>
#share:popover-open { border-radius: 10px; padding: 16px; }
</style>
</head>
<body>
<h1>Lucknowi dum biryani</h1>
<button popovertarget="share">Share</button>
<div id="share" popover>
<h2>Share this recipe</h2>
<ul>
<li><a href="#">Email</a></li>
<li><a href="#">Copy link</a></li>
<li><a href="#">Print</a></li>
</ul>
<button popovertarget="share" popovertargetaction="hide">Close</button>
</div>
</body>
</html>Plain popover means auto, so clicking outside or pressing Esc also closes it. The Close button uses popovertargetaction="hide" so it can never reopen the panel by mistake.
Quick Quiz
1. What does writing popover with no value mean?
An empty popover attribute is the same as auto.
2. What is light dismiss?
Light dismiss closes auto popovers as soon as the user does something else.
3. Which popover type should a "Saved" toast use if it must stay while the user keeps clicking?
Manual popovers have no light dismiss, so outside clicks do not close them.
4. A popovertarget button inside a <form> does nothing. What is the fix?
Buttons in a form are submit buttons by default, and submit buttons cannot open popovers.
Learn HTML with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
