Last year, I wrote about how to create a progressively enhanced accordion with a few lines of vanilla JavaScript.
Today, I wanted to revisit that approach using Web Components. Let's dig in!
How it works
The details
and summary
elements provide a browser native disclosure component.
<details> <summary>Toggle me</summary> I'm the content </details>
Toggle me
I'm the content
Because it's just HTML, the details
and summary
elements are progressively enhanced by default. Browsers that support them get the interactivity, but older browsers see the content full expanded and accessible.
When the component is open or expanded, it has an [open]
attribute on it. You can also add the [open]
attribute to make your accordion expanded by default.
<details open> <summary>Toggle me, too</summary> I'm open by default. </details>
Toggle me, too
I'm open by default.
In the article I wrote last year, we used some DOM manipulation to listen for whenever a details
element was expanded and close all of the others in a group.
Today, let's convert that over to a Web Component.
Creating the custom element
Let's name our custom element pe-accordion
(for Progressively Enhanced Accordion). We'll wrap it around a collection of details
and summary
elements.
<pe-accordion> <details open> <summary>Merlin</summary> Dancing Teacups </details> <details> <summary>Ursula</summary> Stealing Voices </details> <details> <summary>Radagast</summary> Talks to Animals </details> </pe-accordion>
By default, the UI will show a collection of elements that can be independently expanded and collapsed in modern browsers, and will render as plain old text in older ones.
Defining our Web Component
Next, we'll use the customElements.define()
method to define our Web Component.
We'll pass in the pe-accordion
element and a class
that extends
the HTMLElement
object as arguments.
customElements.define('pe-accordion', class extends HTMLElement { // ... });
Inside our class
, we'll use the constructor()
method to create our Web Component instance. We'll run the super()
method to make sure we inherit the parent class properties.
customElements.define('pe-accordion', class extends HTMLElement { constructor () { // Inherit class properties super(); } });
Now, we have a defined Web Component that does nothing. Let's add some interactivity!
Creating our listener
Inside the constructor, let's define an event handler
property on the instance. We want to access this
inside the event handler function, so we'll cache it to the instance
variable first.
I copy/pasted this from our previous script, and made a few modifications.
We don't need to check for the parent element, since we'll scope our listener to the custom element. And we'll search for all opened
accordions on the instance
. Otherwise, this is the same script as before.
constructor () { // Inherit class properties super(); // Cache instance for use in function let instance = this; // Setup handler function this.handler = function (event) { // Only run if accordion is open if (!event.target.hasAttribute('open')) return; // Get all open accordions inside parent let opened = instance.querySelectorAll('details[open]'); // Close open ones that aren't current accordion for (let accordion of opened) { if (accordion === event.target) continue; accordion.removeAttribute('open'); } }; }
Next, I added two addition lifecycle methods to the Web Component.
The connectedCallback()
method runs when the element is attached to the DOM, and the disconnectedCallback()
method runs if it's removed.
In the connectedCallback()
method, I listen for toggle
events on the custom pe-accordion
element (this
), and pass in this.handler
as the function to run. The toggle
event doesn't bubble, so I need to pass in true
for the optional useCapture
parameter.
/** * Runs each time the element is appended to or moved in the DOM */ connectedCallback () { this.addEventListener('toggle', this.handler, true); }
In the disconnectedCallback()
method, I stop listening for events.
/** * Runs when the element is removed from the DOM */ disconnectedCallback () { this.removeEventListener('toggle', this.handler, true); }
Styling
A nice thing about custom elements is that they provide a simple styling hook.
In our case, let's give our details
elements a border between them and a bit of space. We'll also add a slight and heavier font weight to the summary
element.
pe-accordion details { padding: 0.5em 0; } pe-accordion details:not(:last-child) { border-bottom: 1px solid #808080; } pe-accordion summary { font-weight: bold; margin-bottom: 0.25em; }
Putting it all together
Now, I have a custom Web Component that progressively enhances details
and summary
elements into an accordion group.
Here's a demo you can play with.
I have a favor to ask. If you enjoyed this email, could you forward it to someone else who you think might also like it, or share a link to my newsletter on your favorite social media site?
Cheers,
Chris
Want to share this with others or read it later? View it in a browser.
0 Komentar untuk "[Go Make Things] Creating a progessively enhanced accordion with Web Components"