- Below
ake-class2inherits from/extendsake-class1. - Adding
<select>element toake-class2.shadowRoot. console.logthis.clickmebutton to make sure it's inherited correctly.clickmebutton doesn't work without adding again lines after commentThese 3 linesinake-class2.
I couldn't understand why this behavior happen. why this happpens ?
<html>
<head>
<title>AKE Front</title>
<script>
class1_html = `
<div >
<button >Click Me</button>
</div>
`
class2_html = `
<select></select>
`
/*--------------------------------------------------------------------------------*/
class AKEclass1 extends HTMLElement { //custom-component class
constructor() {
super(); // always call super() first in the constructor.
//const root = this.createShadowRoot(); //chrome only - deprecated
const root = this.attachShadow({mode: 'open'}); //By calling attachShadow with mode: 'open', we are telling our element to save a reference to the shadow root on the element.shadowRoot property
this.shadowRoot.innerHTML = class1_html;
// These 3 lines
this.container = this.shadowRoot.querySelector("div.container");
this.clickme = this.container.querySelector("button.clickme");
this.clickme.addEventListener("click", this.clickMe.bind(this));
}
clickMe() {
alert("Hello !");
}
}
customElements.define('ake-class1', AKEclass1);
/*--------------------------------------------------------------------------------*/
class AKEclass2 extends AKEclass1 { //custom-component class
constructor() {
super(); // always call super() first in the constructor.
this.shadowRoot.innerHTML = class2_html;
// These 3 lines
//this.container = this.shadowRoot.querySelector("div.container");
//this.clickme = this.container.querySelector("button.clickme");
//this.clickme.addEventListener("click", this.clickMe.bind(this));
}
}
customElements.define('ake-class2', AKEclass2);
/*--------------------------------------------------------------------------------*/
</script>
</head>
<body>
<ake-class2 ></ake-class2>
</body>
</html>
CodePudding user response:
As mentioned in the comments .innerHTML = is the culprit.
What it does:
Create a NEW string by concatening
.innerHTML NEWStringdelete the innerHTML DOM tree
and then Garbage Collection (GC) kicks in:- Delete all existing DOM elements, thus remove all connected listeners
set the NEW String as innerHTML
Some 'gurus' say this makes innerHTML evil, I say you need to understand what it does.
In the SO snippet below you see the listener being connected twice, but only executed once when clicked
<script>
class BaseClass extends HTMLElement {
constructor() {
super().attachShadow({mode:'open'})
.innerHTML = `<button>Click ${this.nodeName}</button>`;
this.listen();// but removed by GC
}
listen(){
console.log("add listener on", this.nodeName);
this.shadowRoot
.querySelector("button")
.onclick = (evt) => this.clicked(evt);
}
clicked(evt){
console.log("clicked", this.nodeName)
}
}
//customElements.define('element-1', BaseClass);
customElements.define('element-2', class extends BaseClass {
connectedCallback(){
this.shadowRoot.innerHTML = ` with concatenated HTML`;
this.listen();
}
});
</script>
<element-2></element-2>
Notes:
Using the inline
onclickhandler, it only allows for one handler whereaddEventListenercan add more (you can use it here if you like)No need for oldskool
.bind(this)by defining lexical scope with a arrow function, not a function referenceall can be chained because
super()sets AND returns thethisscopeattachShadowsets AND returnsthis.shadowRoot
