can someone please explain what happens here? am I missing something?
#handleClick() {
this.dispatchEvent(new Event('onClicked'));
}
mySlot.addEventListener('slotchange', () => {
mySlot.addEventListener('click', this.#handleClick); // doesn't work
mySlot.addEventListener('click', () => this.#handleClick()); // works
});
...
myCustomElement.addEventListener('onClicked', (e) => {
console.log(e, e.detail);
});
#handleClick() {
this.dispatchEvent(new Event('onClicked', {bubbles: true, composed: true}));
}
mySlot.addEventListener('slotchange', () => {
mySlot.addEventListener('click', this.#handleClick); // works
});
...
...
myCustomElement.addEventListener('onClicked', (e) => {
console.log(e, e.detail);
});
I know that custom events don't reach the "light-DOM" out of the box, that's why I can understand why we should put "composed: true and bubbles: true". But why do mySlot.addEventListener('click', () => this.#handleClick()) works without the need of making the event "composable"?
Here is a fiddle to better understand the problem: https://jsfiddle.net/rv6w3xj1/1/
Your problem is scope
A Function reference gets a different
this
scope than a Arrow Function definition,thus your
this.dispatchEvent
is executed on/from different DOM Elements.When that element is inside shadowDOM, it needs
bubbles:true;composed:true
to escape through the shadowRootI pruned your code to the bare minimum,
slot
andslotchange
have got nothing to do with thisYou can fix it with oldskool
this.handleClick.bind(this)
,but that is more error-prone than
(e) => this.handleClick(e)
because most junior DEVs won't understand what is going on with
bind
To make 'scope' clearer
Below code displays where
this.dispatchEvent(new CustomEvent('onClicked'
originates fromThus showing what
this
is inthis.dispatchEvent
When inside shadowDOM it needs
bubbles:true;composed
to escape shadowDOMThe
e.composedPath()
clearly shows a different path the "click" Event passed.One click came from
<slot>
(inside shadowDOM) the other from your<custom-element>
(outside shadowDOM)