If an event is dispatched by a LitElement child it cannot be caught by a parent LitElement which is listening for that event.
=> only happen for LitElement on polyfilled browsers
=> HTMLElement works fine also on polyfilled browsers
https://stackblitz.com/edit/lit-element-event-bubble?file=index.html
Pro Tip: use Firefox ESR to test a polyfilled browser: https://www.mozilla.org/en-US/firefox/organizations/
The events can be caught by a parent
(the same as it works when using HTMLElement)
The event never gets caught
You have a slightly different sequence of operations between the LitElement and native implementations of components.
In native, you're doing:
In LitElement:
You can see that the child fires the event before the parent listens for it. There's an easy fix: add the event listener before rendering:
in LitParent:
connectedCallback() {
console.log('lit-parent setting up Registration Shop')
this.addEventListener('foo-register', ev => {
this.litChildren.push(ev.detail.el);
});
super.connectedCallback();
}
You can also add the event listener in the constructor.
Hope this helps!
I'm afraid that the suggestion is not correct? I tested it before as it was my first idea as well - so without any code changes, you will see this in the console
lit-parent setting up Registration Shop
lit-child wants to register
native-parent setting up Registration Shop
native-child wants to register
=> so the addEventListener is before.
I still tried the suggestions
https://stackblitz.com/edit/lit-element-event-bubble-constructor?file=lit-parent.js
=> console log fires in the same order
=> no change... e.g. does not work
https://stackblitz.com/edit/lit-element-event-bubble-connectedcallback?file=lit-parent.js
=> console log fires in the same order
=> no change... e.g. does not work
This doesn't FIX anything, but is important for testing a fix as I couldn't get FFESR to update at all, at boot time or later in the page lifecycle.
Don't forget to use:
static get properties() {
return {
litChildren: {type: Array}
}
}
and:
this.litChildren = [...this.litChildren, ev.detail.el];
Not sure how Chrome is triggering a render with this set up? 馃
This is very confusing. The event appears to not be firing at all. FF doesn't have an event breakpoint either.
@Westbrook as lit rendering is async the event listener and the event dispatch happens before the first render => I added a this.requestUpdate(); just in case
@justinfagnani yes it's suupper strange... it seems like the event is not even bubbling at all 馃檲
even if I put the HTMLElement version in the LitElement parent and the other way around it doesn't help ... apparently, neither event listening nor event dispatching works => I'm so confused right now 馃檲
ok I think I have a lucky shot.... but I am sooo confused whyyyy?
https://stackblitz.com/edit/lit-element-event-bubble-solution?file=lit-parent.js
putting it like this in lit-child
connectedCallback() {
super.connectedCallback();
requestAnimationFrame(() => {
console.log('lit-child wants to register');
this.dispatchEvent(new CustomEvent('foo-register', {
bubbles: true,
detail: { el: this }
}));
});
}
and it works... funny thing it does NOT change anything on the order of addEventListener.... so the console log order for lit is still the same
lit-parent setting up Registration Shop
lit-child wants to register
but somehow it now bubbles????
maybe it has something to do with the polyfill? but why is ONLY a problem for LitElement and not HTMLElement?
how can a requestAnimationFrame "enable" bubbling?
anyone got any answers?
@justinfagnani any idea why an animation frame saves the day for lit-element? should not be needed right?
There's definitely an issue here. It's probably related to the ShadyDOM polyfill, but it's unclear why the non-lit-element works correctly. We're investigating.
Here's what's happening. The ShadyDOM polyfill currently does not correctly bubble events fired from nodes that are not selected into a shadowRoot <slot>. These nodes are ejected from the real DOM tree and since ShadyDOM relies on native event dispatching, bubbling does not work as expected. LitElement asynchronously renders content into its shadowRoot; however, it constructs its shadowRoot synchronously when the element is created. So, the lit-child is constructed and connected and fires the event while it is outside the dom tree containing lit-parent and its empty shadowRoot.
There are some options for fixing this, but doing so is a high priority. We're going to look at fixing event propagation for undistributed elements within the polyfill. This may be difficult and we're also considering making LitElement create its shadowRoot when it first renders into it, which should also address the issue.
we're also considering making LitElement create its shadowRoot when it first renders into it
This would break some code that expects renderRoot to be available just after element creation
This would break some code that expects renderRoot to be available just after element creation
Yeah, we wouldn't do that without careful consideration, but we consider this a serious issue that should be fixed. Do you have a use case for using renderRoot after creating but before first update?
Do you have a use case for using renderRoot after creating but before first update?
Yes. In production:
Delegating events:
constructor() {
super();
const events = this.constructor.__events;
if (events) {
events.forEach(({ eventName, selector, listener }) => {
delegate(selector ? this.renderRoot || this : this, eventName, selector, listener, this);
});
}
}
Should be fixed when this ShadyDOM issue is addressed: https://github.com/webcomponents/shadydom/issues/324
I just have been bitten by this again 馃槶
any progress on it? or anything I can do to help?
Any update on this? Ive got issue in IE 11, when working with Polyfill and LitElements.
When my child element inside a slot component is dispatching the event, parent element is not getting this CustomEvent at all.
As Workaround I've made doucment.body listener for this case as well as disptaching this event on body, but it should work as in chrome does, I guess.
_beforeRender() {
this.dispatchEvent(new CustomEvent('test', {
bubbles: true,
detail: {key: 'test'}
}));
}
protected render(): TemplateResult {
console.log('render child');
this._beforeRender();
return html`
<div>Child!</div>
`;
}
I am not sure why, but it seems that in IE eventlistener is ready after the parent element (element where eventlistener is registered) is rendered in to the dom. In chrome it does not matter, events are handled even before.
<parent-element>
<child-element></child-element>
</parent-element>
For such a construction I'm getting output like that after I'm dispatching Event inside render function of child:
constructor parent
connectedCallback parent
constructor child
connectedCallback child
render parent
firstUpdate parent
render child
[event handled here]
Is that expected behavior? Is there a way to delay render of child element somehow?
Edit:
update(changedProperties: PropertyValues) {
console.log('update child');
if (!this._firstRender) {
this.dispatchEvent(new CustomEvent('test', {
bubbles: true,
detail: {key: 'test'}
}));
this._firstRender = true;
}
super.update(changedProperties);
}
First update before render is actually working properly for IE.
@justinfagnani @sorvell what's the current priority on this? We want to work on things like dependency injection and context API, but that's not possible due to this issue.
After a lot of tinkering, I was able to find a relatively clean solution by bypassing shady dom and temporarily creating a proxy node to fire the event:
const { appendChild, removeChild } = window.ShadyDOM.nativeMethods;
const event = new Event('foo', { composed: true, bubbles: true });
const proxy = document.createComment('');
appendChild.call(this.parentNode, proxy);
proxy.dispatchEvent(event);
removeChild.call(this.parentNode, proxy);
LitElement's async rendering isn't really the issue here, the issue is that shady dom doesn't append unslotted elements to the DOM. So even if LitElement would render synchronously, it would still not be correct for situations where an element would never render a slot.
Fixing this properly in the polyfill is pretty hard, and for my use case I don't actually need the element itself to be in the DOM I just need to be able to send an event. It seems like this is a good workable solution?
See it in action here: https://jsbin.com/zocicoyole/edit?html,output
Hi all, this should be fixed as of @webcomponents/[email protected] (https://github.com/webcomponents/polyfills/commit/34a3d9104a8a4a7a2b4db2ce726cc498be6824a1). If you're still experiencing this issue after upgrading, let me know here and I'll reopen.