Popover Attribute Polyfill
This polyfills the HTML popover
attribute and
showPopover
/hidePopover
/togglePopover
methods onto HTMLElement, as well as
the popovertarget
and popovertargetaction
attributes on Button elements.
Polyfill Installation
Download a copy
The simplest, recommended way to install the polyfill is to copy it into your project.
Download popover.js
(or popover.min.js
) from
unpkg.com and add it
to the appropriate directory in your project. Then, include it where necessary
with a <script>
tag:
<script src="/path/to/popover.min.js" type="module"></script>
Or without JavaScript modules:
<script src="/path/to/popover.iife.min.js"></script>
Note that the JS will inject CSS styles into your document (or ShadowRoot).
With npm
For more advanced configuration, you can install with npm:
npm install @oddbird/popover-polyfill
After installing, you’ll need to use appropriate tooling to use
node_modules/@oddbird/popover-polyfill/dist/popover.js
.
For most tooling such as Vite, Webpack, and Parcel, that will look like this:
import '@oddbird/popover-polyfill';
If you want to manually apply the polyfill, you can instead import the
isSupported
and apply
functions directly from
node_modules/@oddbird/popover-polyfill/dist/popover-fn.js
file.
With most tooling:
import { apply, isSupported } from '@oddbird/popover-polyfill/fn';
Via CDN
For prototyping or testing, you can use the npm package via a Content Delivery Network. Avoid using JavaScript CDNs in production, for many good reasons such as performance and robustness.
<script
src="https://cdn.jsdelivr.net/npm/@oddbird/popover-polyfill@latest"
crossorigin="anonymous"
defer
></script>
Usage
After installation the polyfill will automatically add the correct methods and attributes to the HTMLElement class.
Caveats
This polyfill is not a perfect replacement for the native behavior; there are some caveats which will need accommodations:
-
A native
popover
has a:popover-open
pseudo selector when in the open state. Pseudo selectors cannot be polyfilled within CSS, and so instead the polyfill will add the.\:popover-open
CSS class to any open popover. In other words a popover in the open state will haveclass=":popover-open"
. In CSS the:
character must be escaped with a backslash.-
The
:popover-open
selector within JavaScript methods has been polyfilled, so both.querySelector(':popover-open')
and.querySelector('.\:popover-open')
will work to select the same element.matches
andclosest
have also been patched, so.matches(':popover-open')
will work the same as.matches('.\:popover-open')
. -
Using native
:popover-open
in CSS that does not support nativepopover
results in an invalid selector, and so the entire declaration is thrown away. This is important because if you intend to style a popover using.\:popover-open
it will need to be a separate declaration. For example,[popover]:popover-open, [popover].\:popover-open
will not work.
-
-
Native
popover
elements use the:top-layer
pseudo element which gets placed above all other elements on the page, regardless of overflow or z-index. This is not possible to polyfill, and so this library simply sets a really highz-index
. This means if a popover is within an element that hasoverflow:
orposition:
CSS, then there will be visual differences between the polyfill and the native behavior. -
Native invokers (that is, buttons or inputs using the
popovertarget
attribute) onpopover=auto
will render in the accessibility tree as elements withexpanded
. The only way to do this in the polyfill is setting thearia-expanded
attribute on those elements. This may impact mutation observers or frameworks which do DOM diffing, or it may interfere with other code which setsaria-expanded
on elements. -
The polyfill uses
adoptedStyleSheets
to inject CSS onto the page (and each Shadow DOM). If it can't use that it'll generate a<style>
tag instead. This means you may see a<style>
tag you didn't put there, and this may impact mutation observers or frameworks.-
For browsers which don't support
adoptedStyleSheets
on Shadow Roots, if you are building a ShadowRoot by setting.innerHTML
, you'll remove the StyleSheet. Either polyfilladoptedStyleSheets
or callinjectStyles(myShadow)
to add the styles back in. -
Similarly, if you're using Declarative ShadowDOM or otherwise creating a shadow root without calling
attachShadow
/attachInternals
, then the polyfill won't inject the styles (because it can't reference theshadowRoot
). You'll need to manually inject the styles yourself withinjectStyles(myShadow)
. -
As a stylesheet is injected into the main document, if your host element is a popover, styling with
:host
gets tricky beause:host
styles always take lower precedence than the main document styles. This is a limitation of CSS and there's not a reasonable workaround. The best workaround for now is to add!important
to conflicting properties in your:host
rule. See #147 for more.
-
Contributing
Visit our contribution guidelines.