Because I love Web Components so much, today, I thought we'd build an HTML Web Component from scratch. Let's build a component that shows and hides text when a button is toggled.
Let's dig in!
If you'd like help building Web Components for your site or converting existing DOM scripts over, get in touch. I'd be happy to help!
The HTML
The heart of an HTML Web Component is the HTML.
With this approach, the idea is to start with a baseline of fully functional HTML that you enhance into something more interactive once the component instantiates.
To that end, let's add a button
that will show or hide our content, and a div
with our content in it. Since we don't want the button
to display until the Web Component is ready, we'll add the [hidden]
attribute to hide it.
<button hidden>Show Content</button> <div> <p>Now you see me, now you don't!</p> </div>
Now, we've got some content that's visible by default.
Let's wrap it in our custom element, which we'll call show-hide
. We'll also add a [trigger]
attribute to the button
, and a [content]
attribute to the content.
Our Web Component can use these once it instantiates to identify the toggle and content, respectively. This lets us put the button
before or after the content, and gives us a bit of flexibility.
<show-hide> <button trigger hidden>Show Content</button> <div content> <p>Now you see me, now you don't!</p> </div> </show-hide>
Defining the Web Component
Now that we've got our baseline of HTML, we can define our Web Component using the customElements.define()
method.
We'll define our show-hide
custom element, and extend the HTMLElement
class. In our constructor()
, we'll use the super()
method to gain access to the parent class properties.
customElements.define('show-hide', class extends HTMLElement { /** * Instantiate the Web Component */ constructor () { // Get parent class properties super(); } });
Defining properties
Next, let's define some custom properties.
We'll use the Element.querySelector()
method to look for the [trigger]
and [content]
elements inside the custom element (this
), and assign them to to the trigger
and content
properties, respectively.
If either element doesn't exist, we'll return
to end our setup early.
/** * Instantiate the Web Component */ constructor () { // Get parent class properties super(); // Get the elements this.trigger = this.querySelector('[trigger]'); this.content = this.querySelector('[content]'); if (!this.trigger || !this.content) return; }
Setting up the DOM
With our properties defined, we can now enhance the DOM and setup our event listener.
First, let's use the Element.removeAttribute()
method to remove the [hidden]
attribute from the button
element, this.trigger
.
We'll also use the Element.setAttribute()
method to add an [aria-expanded]
attribute with a value of false
. This tells screen readers that the button toggles content visibility, and what the current state of that content is.
/** * Instantiate the Web Component */ constructor () { // Get parent class properties super(); // Get the elements this.trigger = this.querySelector('[trigger]'); this.content = this.querySelector('[content]'); if (!this.trigger || !this.content) return; // Setup default UI this.trigger.removeAttribute('hidden'); this.trigger.setAttribute('aria-expanded', false); }
Next, we'll add the [hidden]
attribute to the this.content
element to hide it.
Then, we'll add a click
event listener to the this.trigger
element. For simplicity, we'll use the handleEvent()
method native to Web Components to handle our event (more on that in a second), and can pass this
in as our callback.
/** * Instantiate the Web Component */ constructor () { // ... // Setup default UI this.trigger.removeAttribute('hidden'); this.trigger.setAttribute('aria-expanded', false); this.content.setAttribute('hidden', ''); // Listen for click events this.trigger.addEventListener('click', this); }
Handling events
The handleEvent()
method is part of the EventListener
API, and has been around for decades.
If you listen for an event with the addEventListener()
method, you can pass in an object instead of a callback function as the second argument.
As long as that object has a handleEvent()
method, the event will be passed into it, but this will maintain it's binding to the object.
customElements.define('show-hide', class extends HTMLElement { /** * Instantiate the Web Component */ constructor () { // ... } /** * Handle events in the Web Component * @param {Event} event The Event object */ handleEvent (event) { // Handle the event... } });
Inside our handleEvent()
method, we'll first run the event.preventDefault()
method to ensure the button doesn't trigger any unexpected side-effects, like submitting a form.
/** * Handle events in the Web Component * @param {Event} event The Event object */ handleEvent (event) { // Don't let the button trigger other actions event.preventDefault(); }
Then, we'll use the Element.getAttribute()
method to get the value of the [aria-expanded]
attribute on the this.trigger
element.
If it has a value of true
, the content is currently expanded and we should hide it. If not, it's hidden and we should show it.
We'll set or remove the [hidden]
attribute on this.content
accordingly, and update the value of the [aria-expanded]
attribute to match the current state.
/** * Handle events in the Web Component * @param {Event} event The Event object */ handleEvent (event) { // Don't let the button trigger other actions event.preventDefault(); // If the content is expanded, hide it // Otherwise, show it if (this.trigger.getAttribute('aria-expanded') === 'true') { this.trigger.setAttribute('aria-expanded', false); this.content.setAttribute('hidden', ''); } else { this.trigger.setAttribute('aria-expanded', true); this.content.removeAttribute('hidden'); } }
Now, the content will show or hide when the button is toggled.
Styling
The nice thing about using appropriate ARIA attributes (like [aria-expanded]
) for interactive elements is that you can also use them to style things based on the current state of the element.
For example, you could use the [aria-expanded]
attribute to show different icons on the button based on whether or not the content is visible.
show-hide [aria-expanded="true"] { /* Styles for visible content */ } show-hide [aria-expanded="false"] { /* Styles for hidden content */ }
Building HTML Web Components
I've helped organizations like NASA use Web Components to create interactive UI that's simpler to implement and maintain.
If you'd like help on your project, get in touch and setup a call. I'd love to hear more about your project!
And if you'd like to learn how to do this yourself, I have a bunch of tutorials and demos over on 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] Let's create a Web Component from scratch!"