I'm trying to use vanilla javascript to build a custom ponent which observes changes in all data attributes, e.g.:
class MyComponent extends HTMLElement {
static get observedAttributes () {
return ["data-one","data-two","data-three",so forth...]
}
}
This ponent could in theory be assigned an arbitrary number of data attributes, so there's no way to predict exactly how many there would be, yet I need the ponent to do stuff every time a new data attribute is assigned to it, is there any way to do it? having to put into the array returned by "observedAttributes" the specific name of every attribute seems really restrictive
As a bonus, is there any way to observe attributes that don't have a specific name but follow a certain pattern? (e.g. they match against a regex string or something like that)
And as an extra bonus, is there any way to observe all attributes? (I know they made this not be the default behavior due to performance factors, but still it would be good to be able to enable it if needed)
I'm trying to use vanilla javascript to build a custom ponent which observes changes in all data attributes, e.g.:
class MyComponent extends HTMLElement {
static get observedAttributes () {
return ["data-one","data-two","data-three",so forth...]
}
}
This ponent could in theory be assigned an arbitrary number of data attributes, so there's no way to predict exactly how many there would be, yet I need the ponent to do stuff every time a new data attribute is assigned to it, is there any way to do it? having to put into the array returned by "observedAttributes" the specific name of every attribute seems really restrictive
As a bonus, is there any way to observe attributes that don't have a specific name but follow a certain pattern? (e.g. they match against a regex string or something like that)
And as an extra bonus, is there any way to observe all attributes? (I know they made this not be the default behavior due to performance factors, but still it would be good to be able to enable it if needed)
Share Improve this question asked Jul 21, 2021 at 3:52 super potionsuper potion 3753 silver badges13 bronze badges 3- 2 No, use the MutationObserver API to capture attributes that where not assigned as observed attributes when the Custom Element was created. MDN: developer.mozilla/en-US/docs/Web/API/MutationObserver – Danny '365CSI' Engelman Commented Jul 21, 2021 at 10:21
- Hmm, didn't know that existed, thanks. Well alright, that's one way to do it, was hoping there was a way to do it inside the observedAttributes getter that I was missing, but I guess there's no such thing – super potion Commented Jul 21, 2021 at 19:20
-
@Danny'365CSI'Engelman Is there any advantage of using
observedAttributes()
vs creating a MutationObserver then? Seems like the MutationObserver is much more flexible. – David Min Commented Jun 30, 2022 at 16:31
3 Answers
Reset to default 3As pointed out in ments by Danny, you can use MutationObserver to do this:
class MyCustomElement extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
const observer = new MutationObserver((mutationRecords) => {
mutationRecords.forEach(record => {
console.log(`${record.attributeName} has changed to ${this.getAttribute(record.attributeName)}`);
});
}).observe(this, { attributes: true });
}
}
Here is a way to observe all data-attribute changes (using MutationObserver
). The callback
function filters the observed mutations.
See also
const observer = new MutationObserver(callback);
observer.observe(document, {
attributes: true,
subtree: true,
attributeOldValue: true,
});
// change some data-attributes
const testElem = document.querySelector(`#test`);
// will be observed
testElem.dataset.new = `something else`;
// will NOT be observed
testElem.setAttribute(`title`, `added a title`);
setTimeout(_ => {
testElem.dataset.first = "Hi";
testElem.dataset.second = "universe!";
}, 2000);
setTimeout(_ => testElem.dataset.second = "milky way", 1000);
function callback(mutationList) {
// filter relevant mutations (data-attributes)
[...mutationList].filter(m => m.attributeName.startsWith(`data`))
.forEach( mutation => {
console.info(`✓ Attribute [${
mutation.attributeName}] changed to '${
mutation.target.getAttribute(mutation.attributeName)}' (was '${
mutation.oldValue}')`);
});
}
[data-second]:before {
content: attr(data-first)' ';
}
[data-second]:after {
content: attr(data-second);
}
<div id="test" data-first="hello" data-second="world"></div>
sure you can do so by using one of Custom element callbacks based on this
here are all Custom element lifecycle callbacks
and you need to focus on this one attributeChangedCallback
// Create a class for the element class MyCustomElement extends HTMLElement { static observedAttributes = ["color", "size"]; constructor() { // Always call super first in constructor super(); } connectedCallback() { console.log("Custom element added to page."); } disconnectedCallback() { console.log("Custom element removed from page."); } adoptedCallback() { console.log("Custom element moved to new page."); } attributeChangedCallback(name, oldValue, newValue) { console.log(`Attribute ${name} has changed.`); } } customElements.define("my-custom-element", MyCustomElement);