🔥 Black Friday Sale! Today through Monday, you can join the Lean Web Club for just $9/month. Try it free for 30 days, then get 50% off for the next year.
Today, I wanted to share a little Web Component I made for expanding or collapsing all of the <details>
elements on a page.
Let's dig in!
Why do you need this?
Over on the Lean Web Club, I host a massive, searchable collection of courses and tutorials on a wide range of front-end topics.
I originally just had them all as one giant list, but the page got comically long and really hard to scan.
To address that, I organize the tutorials by topic, with collections of tutorials linked inside <details>
and <summary>
elements…
<h3>APIs & Asynchronous JS</h3> <details> <summary>APIs</summary> ... </details> <details> <summary>Authentication</summary> ... </details> <details> <summary>Promises</summary> ... </details> <details> <summary>Security</summary> ... </details> <h3>Accessibility</h3> ...
But I also wanted to make sure folks had a way to see everything on the page, without having to manually expand a hundred or more <details>
elements.
So, I made a Web Component to handle that!
The HTML
Here's the essential HTML for the Web Component.
It requires the <details-expand>
custom element, and two <button>
elements. Each <button>
has an [action]
attribute, with a value of expand
or collapse
.
Since it only works with JavaScript, I also use the [hidden]
attribute to hide it by default.
<details-expand hidden> <button action="expand">Expand All</button> <button action="collapse">Collapse All</button> </details-expand>
I also added some custom icons and classes to make things look pretty, but that can vary from implementation to implementation. Feel free to style it to taste.
The JavaScript
To hand-wave over this a bit (check out my article on your first web component if your brand new to this), here's the starting boilerplate for my Web Component…
customElements.define('details-expand', class extends HTMLElement { /** * Instantiate the Web Component */ constructor () { // Get parent class properties super(); // ... } });
When the Web Component instantiates, I remove the [hidden]
attribute, and setup a click
event listener on the entire custom element using event delegation.
/** * Instantiate the Web Component */ constructor () { // Get parent class properties super(); // Show the content this.removeAttribute('hidden'); // Listen for events this.addEventListener('click', this); }
I use the handleEvent()
method to handle my events (one of my favorite Web Component features).
Inside my event handler, I make sure the clicked element has or is inside an element with the [action]
attribute using the Element.closest()
method. Then, I get the value of that attribute.
If it's expand
, I set an action
variable of setAttribute
. Otherwise, I set it to removeAttribute
.
/** * Handle events */ handleEvent (event) { // Get the action type let btn = event.target.closest('[action]'); if (!btn) return; let action = btn.getAttribute('action') === 'expand' ? 'setAttribute' : 'removeAttribute'; }
Next, I use the document.querySelectorAll()
method to get all of the <details>
elements on the page. Then, I loop through each one, and use the Element.setAttribute()
or Element.removeAttribute()
method to set or remove the [open]
attribute.
This attribute is automatically added to <details>
elements when they're expanded. Adding or removing it manually will open or close the element, respectively.
/** * Handle events */ handleEvent (event) { // Get the action type let btn = event.target.closest('[action]'); if (!btn) return; let action = btn.getAttribute('action') === 'expand' ? 'setAttribute' : 'removeAttribute'; // Get all of the collapse sections let sections = document.querySelectorAll('details'); // Hide or show the content for (let section of sections) { section[action]('open', ''); } }
Now, clicking one of the two buttons will expand or collapse all of the <details>
elements.
One more thing!
My <details>
elements all have unique IDs on them that I use as anchor links…
<details id="promises"> <summary>Promises</summary> ... </details>
If someone visits the page and there's an anchor link in the URL pointing to that element, I want to expand it by default.
I put the code for this inside the connectedCallback()
method to ensure it doesn't run until the element is actually ready and loaded into the DOM.
First, I look for a window.location.hash
.
If one exists, I find the matching element using the document.querySelector()
method. If the element exists and is a <details>
element, I add the [open]
attribute.
/** * Expand an anchored section on load */ connectedCallback () { if (!window.location.hash) return; let target = document.querySelector(window.location.hash); if (!target || !target.matches('details')) return; target.setAttribute('open', ''); }
Now, anchored elements are expanded by default!
Here's a demo you can play with. I've also added this to the toolkit over at the Lean Web Club.
Cheers,
Chris
Want to share this with others or read it later? View it in a browser.
0 Komentar untuk "[Go Make Things] A Web Component to expand or collapse all details elements"