Yesterday, we looked at the Web Component lifecycle. While Web Components don't have data reactivity, one of the lifecycle methods built into the Web Component API can be used to detect when an attribute on your custom element changes.
Today, we're going to learn how to do that. Let's dig in!
The attributeChangedCallback()
method
The attributeChangedCallback()
method is part of the Web Component lifecycle, and runs whenever an attribute on the Web Component is added, removed, or changes in value.
It accepts three arguments: the name
of the attribute that's been changed, its oldValue
, and its newValue
.
/** * Runs when the value of an attribute is changed on the component * @param {String} name The attribute name * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ attributeChangedCallback (name, oldValue, newValue) { console.log('attribute changed', name, oldValue, newValue, this); }
For performance reasons, the attributeChangedCallback()
method only watches and reacts to attributes you tell it to.
To do that, you create a static observedAttributes
property, with an array of attributes to watch as its value.
You can use any attributes you'd like, including non-standard ones. Here, we'll tell our Web Component to watch for changes to the [text]
and [pause]
attributes.
// Define the attributes to observe static observedAttributes = ['text', 'pause']; /** * Runs when the value of an attribute is changed on the component * @param {String} name The attribute name * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ attributeChangedCallback (name, oldValue, newValue) { console.log('attribute changed', name, oldValue, newValue, this); }
Now, we can do something like this…
let count = document.querySelector('wc-count'); // logs "attribute changed" "text" null "You clicked it: " count.setAttribute('text', 'You clicked it: ');
But if we modify an attribute that's not on our observedAttributes
list, nothing happens.
// Nothing happens count.setAttribute('id', 'count-1234');
Here's a demo.
Reacting to attribute changes on your Web Component
Now that we're detecting our attribute changes, we can actually react to them.
For example, when the [text]
attribute is added or modified on the <wc-count>
element, we might update the this.text
property to match it, and re-render the text in this.button
with the updated text.
/** * Runs when the value of an attribute is changed on the component * @param {String} name The attribute name * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ attributeChangedCallback (name, oldValue, newValue) { // If the [text] attribute, update this.text and render button text if (name === 'text') { this.text = newValue; this.button.textContent = this.text.replace('', this.count); } }
And maybe when the [pause]
attribute is added, we stop counting clicks and [disable]
the button.
/** * Runs when the value of an attribute is changed on the component * @param {String} name The attribute name * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ attributeChangedCallback (name, oldValue, newValue) { // If the [text] attribute, update this.text and render button text if (name === 'text') { this.text = newValue; this.button.textContent = this.text.replace('', this.count); } // If the [pause] attribute, stop counting if (name === 'pause') { this.button.removeEventListener('click', this); this.button.setAttribute('disabled', ''); } }
When the [pause]
attribute is removed, we probably want to start counting again.
To do that, we'll check the value of the newValue
parameter. If it's null
, the attribute was removed. Otherwise, it's been added.
/** * Runs when the value of an attribute is changed on the component * @param {String} name The attribute name * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ attributeChangedCallback (name, oldValue, newValue) { // If the [text] attribute, update this.text and render button text if (name === 'text') { this.text = newValue; this.button.textContent = this.text.replace('', this.count); } // If the [pause] attribute, stop counting if (name === 'pause') { if (newValue === null) { this.button.addEventListener('click', this); this.button.removeAttribute('disabled'); } else { this.button.removeEventListener('click', this); this.button.setAttribute('disabled', ''); } } }
Here's a demo you can play with.
Organizing your code
If you're observing multiple attributes, the attributeChangedCallback()
can get pretty unruly.
To make it easier to work with, I like to abstract the code to handle those changes into handler methods. I prefix them with handlechange*
, where *
is the attribute name.
/** * Handle [text] attribute changes * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ handlechangetext (oldValue, newValue) { this.text = newValue; this.button.textContent = this.text.replace('', this.count); } /** * Handle [pause] attribute changes * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ handlechangepause (oldValue, newValue) { if (newValue === null) { this.button.addEventListener('click', this); this.button.removeAttribute('disabled'); } else { this.button.removeEventListener('click', this); this.button.setAttribute('disabled', ''); } }
Then, in the attributeChangedCallback()
method, I can automatically run the correct method on this
by using bracket notation and a template literal. I pass in the oldValue
and newValue
.
/** * Runs when the value of an attribute is changed on the component * @param {String} name The attribute name * @param {String} oldValue The old attribute value * @param {String} newValue The new attribute value */ attributeChangedCallback (name, oldValue, newValue) { this[`handlechange${name}`](oldValue, newValue); }
Here's one last demo for you.
Like this? You can support my work by purchasing an annual membership.
Cheers,
Chris
Want to share this with others or read it later? View it in a browser.
0 Komentar untuk "[Go Make Things] How to detect when attributes change on a Web Component"