In the following code, attributeChangedCallback never gets called even if the 'content' attribute is created, changed or removed.
class Square extends HTMLElement {
static get observedAttributes() {
return ['content'];
}
constructor(val) {
super();
console.log('inside constructor');
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(document.createElement('button'));
this.button = this.shadowRoot.querySelector('button');
this.button.className = "square";
this.content = val;
console.log('constructor ended');
}
get content() {
console.log('inside getter');
return this.button.getAttribute('content');
}
set content(val) {
console.log('setter being executed, val being: ', val);
// pass null to represent empty square
if (val !== null) {
this.button.setAttribute('content', val);
} else {
if (this.button.hasAttribute('content')) {
this.button.removeAttribute('content');
}
}
}
connectedCallback() {
//console.log('connected callback being executed now');
}
// not working :(
attributeChangedCallback(name, oldValue, newValue) {
console.log('attribute changed callback being executed now');
if (name === 'content') {
this.button.innerHTML = newValue?newValue:" ";
}
}
}
customElements.define('square-box', Square);
Based on the best practices given here, I want the side-effect of attribute change (updation of innerHTML in my case) to take place in attributeChangedCallback. However when I move this updation to the setter, the code works fine.
In the following code, attributeChangedCallback never gets called even if the 'content' attribute is created, changed or removed.
class Square extends HTMLElement {
static get observedAttributes() {
return ['content'];
}
constructor(val) {
super();
console.log('inside constructor');
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(document.createElement('button'));
this.button = this.shadowRoot.querySelector('button');
this.button.className = "square";
this.content = val;
console.log('constructor ended');
}
get content() {
console.log('inside getter');
return this.button.getAttribute('content');
}
set content(val) {
console.log('setter being executed, val being: ', val);
// pass null to represent empty square
if (val !== null) {
this.button.setAttribute('content', val);
} else {
if (this.button.hasAttribute('content')) {
this.button.removeAttribute('content');
}
}
}
connectedCallback() {
//console.log('connected callback being executed now');
}
// not working :(
attributeChangedCallback(name, oldValue, newValue) {
console.log('attribute changed callback being executed now');
if (name === 'content') {
this.button.innerHTML = newValue?newValue:" ";
}
}
}
customElements.define('square-box', Square);
Based on the best practices given here, I want the side-effect of attribute change (updation of innerHTML in my case) to take place in attributeChangedCallback. However when I move this updation to the setter, the code works fine.
Share Improve this question asked Feb 20, 2020 at 11:59 user103643user103643 431 gold badge1 silver badge3 bronze badges1 Answer
Reset to default 4set content(val) {
console.log('setter being executed, val being: ', val);
// pass null to represent empty square
if (val !== null) {
this.button.setAttribute('content', val);
} else {
if (this.button.hasAttribute('content')) {
this.button.removeAttribute('content');
}
}
}
You are mixing up parents & children
You are defining a setter on your Element <square-box> (►parent element)
When you do this.button.setAttribute('content', val);
You are changing a attribute of your <button> Element (►child element)
This will never trigger the attributeChangedCallback
of (►parent) <square-box>
because its attributes were not changed
You either have to go "up the DOM" with .getRootNode() and/or .host to set attributes of parent elements.
or use Custom Events (bubbling up the DOM) to notify parents that children have done/changed something
I presume you meant to do
set content(val) {
// loose ==null parison for null AND undefined,
// element.content=null; will remove the attribute
if (val==null)
this.removeAttribute('content');
else
// but you DO want .content(0) (0==false) set as "0"
this.setAttribute('content', val);
}