Polyfill Demo Popover Attribute
OddBird’s Popover Attribute Polyfill – built in collaboration with Keith Cirkel and used in production by GitHub – lets developers preview the upcoming mechanism for displaying popover content on top of other page content, drawing the user’s attention to specific important information or actions that need to be taken.
This polyfills the HTML popover
attribute and
showPopover
, hidePopover
, and
togglePopover
methods on HTMLElement
, as
well as the popovertarget
and
popovertargetaction
attributes on
<button>
elements.
See which browsers have native Popover API support.
@layer
support)
🔗 Default Popover
Popovers take a state of “auto” or “manual”. If no state is provided, the popover takes on the behavior of its default state, which is “auto”. Auto popovers can be “light dismissed” by selecting anywhere on the page, clicking the popover control button, or opening another popover on the page.
<button popovertarget="defaultPopover" aria-expanded="false">
Toggle Default Popover
</button>
<div id="defaultPopover" popover>Default Popover</div>
🔗 Manual Popover
Manual popovers must be dismissed by toggling the control button or clicking another control that is explicity set to hide the popover.
<button popovertarget="manualPopover">
Toggle Manual Popover
</button>
<div id="manualPopover" popover="manual">Manual Popover</div>
🔗 Popover hint
Hint popovers only dismiss other hint popovers.
<button data-hover-target="hintPopover" data-btn>
Hover to toggle Hint Popover
</button>
<button data-hover-target="hintPopover2" data-btn>
Hover to toggle Hint Popover 2
</button>
<button popovertarget="autoPopover" data-btn>
Show Auto Popover
</button>
// JavaScript
document.querySelectorAll('[data-hover-target]').forEach((el) => {
const target = document.getElementById(el.dataset.hoverTarget);
el.addEventListener('mouseover', () => {
target.togglePopover({source: el});
});
el.addEventListener('focus', () => {
target.togglePopover({source: el});
});
});
🔗 Dialog Popover
<dialog>
elements can also be used as popovers.
<button popovertarget="dialogPopover">
Toggle Dialog Popover
</button>
<dialog id="dialogPopover" popover>Dialog Popover</dialog>
🔗 Popover with Empty State
A popover with an empty state behaves as if it were set to “auto”.
<button popovertarget="emptyStatePopover">Toggle Empty State Popover</button>
<div id="emptyStatePopover" popover="">Empty State Popover</div>
🔗 Popover Controls with Explicit Actions
Popover button controls can be set to “show” or “hide” with the
popovertargetaction
attribute. This
popover
also has a link with an
autofocus
attribute, which receives focus when the
popover is opened.
<button popovertargetaction="show" popovertarget="showHidePopover">
Show Popover with Explicit Action
</button>
<button popovertargetaction="hide" popovertarget="showHidePopover">
Hide Popover with Explicit Action
</button>
<div id="showHidePopover" popover="manual">
Popover with Explicit Action. This popover has an <a href="#" autofocus>autofocus link.</a>
</div>
🔗 Popover Control with Only the “Show” Action
Since the value of the popovertargetaction
attribute is
set to “show”, clicking the button will only open the popover. The
popover can be closed by selecting elsewhere on the page, or opening
another popover. This popover
also has a link with an
autofocus
attribute, which receives focus when the
popover is opened.
<button popovertargetaction="show" popovertarget="singleActionShowPopover">
Show Single Action Popover
</button>
<div id="singleActionShowPopover" popover>
Single Action Popover. This popover has an <a href="#" autofocus>autofocus link.</a>
</div>
🔗 Popover Control with Invalid Target
Since no popover matches the
popovertarget
of “invalidTargetPopover”, no popover
will open.
<button popovertarget="invalidTargetPopover">
Toggle Nothing
</button>
🔗 Targeting Elements in a Different Tree Scope
This button created in the light DOM has its
popovertarget
, which is in the shadow DOM, set using
JavaScript.
Note that while this use-case is supported by the polyfill,
native browser support has
lagged behind.
<!-- Light DOM --> <button id="crossTreeToggle"> Toggle Cross Tree Popover </button> <div id="crossTreeHost"> <!-- Shadow DOM --> <div id="crossTreePopover" popover>Shadowed Popover</div> </div>
// JavaScript const crossTreeHost = document.getElementById('crossTreeHost'); const crossTreeShadowRoot = crossTreeHost.attachShadow({ mode: 'open' }); crossTreeShadowRoot.innerHTML = '<div id="crossTreePopover" popover>Shadowed Popover</div>'; document.getElementById('crossTreeToggle').popoverTargetElement = crossTreeShadowRoot.getElementById('crossTreePopover');
🔗 Nested Popovers
Popovers can be nested within other popovers.
<button popovertargetaction="show" popovertarget="menuPopover">
Show Menu
</button>
<div id="menuPopover" popover>
<ul>
<li><button>Item 1</button></li>
<li><button>Item 2</button></li>
<li>
<button popovertarget="nestedPopover" popovertargetaction="show">
Show Sub Menu
</button>
</li>
</ul>
<div id="nestedPopover" popover style="left: 200px">
<ul>
<li><button>Submenu Item 1</button></li>
<li><button>Submenu Item 2</button></li>
<li><button>Submenu Item 3</button></li>
</ul>
</div>
</div>
🔗 Opening a Popover with an Element Created in the Shadow Root
Elements created in the shadow DOM can trigger a popover.
<!-- Light DOM --> <button popovertarget="shadowInvokedPopover"> <span id="shadowInvokedHost"> <!-- Shadow DOM --> <span>Toggle Shadow Invoked Popover</span> </span> </button> <div id="shadowInvokedPopover" popover> Shadow Invoked Popover </div>
// JavaScript const shadowInvokedHost = document.getElementById('shadowInvokedHost'); const shadowRoot = shadowInvokedHost.attachShadow({ mode: 'open' }); shadowRoot.innerHTML = `<span>Toggle Shadow Invoked Popover</span>`;
🔗 Nested Popovers in Shadow DOM
Popovers can be nested within other popovers in the shadow DOM.
<!-- Light DOM --> <button popovertarget="shadowNestedPopover"> Toggle Menu with Shadowed Popover </button> <div id="shadowNestedPopover" popover> Menu with Shadowed Popover: <div id="menuHost"> <!-- Shadow DOM --> <button popovertarget="shadowedMenuInner"> Toggle Inner Shadowed Popover </button> <div id="shadowedMenuInner" popover> Inner Shadowed Popover </div> </div> </div>
// JavaScript const menuHost = document.getElementById('menuHost'); const menuShadowRoot = menuHost.attachShadow({ mode: 'open' }); menuShadowRoot.innerHTML = ` <button popovertarget="shadowedMenuInner"> Toggle Inner Shadowed Popover </button> <div id="shadowedMenuInner" popover> Inner Shadowed Popover </div>`;
🔗 Layers Support
When supported, the polyfill creates a cascade layer named
popover-polyfill
. If author styles are not in layers
then this should have no impact. If layers are used, authors will
need to ensure the polyfill layer is declared first (e.g.
@layer popover-polyfill, other, layers;
).
<button popovertarget="layeredStylesPopover"> Toggle Popover with Layered Styles </button> <div id="layeredStylesPopover" data-popover="layered-styles" popover> Popover with Layered Styles (border should be red if browser has <code>@layer</code> support) </div>
/* CSS */ @layer popover-polyfill; @layer consumer { [data-popover~='layered-styles'] { --border-color: #d00d1e; } }
🔗 Popovers and Controls Created Fully in the Shadow Root
Here, both the popover control button and the popover are created in the Shadow DOM.
<!-- Shadow DOM -->
<button popovertarget="shadowedPopover">
Toggle Shadowed Popover
</button>
<button popovertarget="shadowedNestedPopover">
Toggle Shadowed Nested Popover
</button>
<div id="shadowedPopover" popover>Shadowed Popover</div>
<div>
<div id="shadowedNestedPopover" popover>Shadowed Nested Popover</div>
</div>
🔗 Shadow Root Popover with Slotted Contents
Here, both the popover control button and the popover are created in the Shadow DOM, but an element is slotted in the popover.
<!-- Light DOM -->
<div id="shadowHostWithSlot">
<div><span>I’m a slotted element</span></div>
</div>
<!-- Shadow DOM -->
<button popovertarget="shadowedPopoverWithSlot">
Toggle Shadowed Popover
</button>
<div id="shadowedPopoverWithSlot" popover>
<slot></slot>
</div>
Sponsor OddBird’s OSS Work
At OddBird, we love contributing to the languages & tools developers rely on. We’re currently working on polyfills for new Popover & Anchor Positioning functionality, as well as CSS specifications for functions, mixins, and responsive typography. Help us keep this work sustainable and centered on your needs as a developer! We display sponsor logos and avatars on our website.
Sponsor OddBird’s OSS Work