#Custom Events with Web Components

11 messages · Page 1 of 1 (latest)

merry trellis
#

I am having trouble dispatching a customEvent to an astro web component that is listening. How am I supposed to dispatch a customEvent from one astro file to another another component that is listening?

I have an astro page "pages/examples/index.astro" with a button on the page, along with an imported astro web component "components/example-web-component.astro". I am trying to send a customEvent "example-event" from the index page to the example component. The component is listening for the event, confirmed by the browers inspector under elements > event listeners. However I'm unable to dispatch the event from the button. I have tried dispatching the event from the component element, the button element itself, and the window, along with trying different events that include bubble, all with no success.

vagrant dustBOT
#
No-one around right now?

It looks like no-one has responded to your question yet. People might not be available right now or don’t know how to answer your question. Want an answer while you wait? Try asking our experimental bot in #1095492539085230272.

lofty wolf
#

Sounds to me like you have 2 sibing elements here and you expect one sibling to receive the events from another sibling. Is that correct? If so: I suppose this won't work because events only "travel" through their ancestors (i.e. their parents).

If I got it wrong: Could you post the DOM structure of the relevant parts in your index.astro?

merry trellis
#

index.astro:
-- <button>
-- <component>
button.onClick( component.dispatchEvent("myCoolEvent")

component.astro:
component.addEventListener("myCoolEvent")

lofty wolf
#

I see.. That won't work out the way you intend.

What would work is this

<component>
  <button />
</component>

Or you have to somehow get a reference to componentwithin your button component. LIke this:

button:

<script>
const component = document.querySelector('component');
const onClick = component.dispatchEvent(...);
</script>
merry trellis
#

So I tried the reference method and that works but only if that component isn't a web component. The web component looks like this:

<example-component>
...
</example-component>
<script>
class ExampleComponent extends HTMLElement {
  constructor() {
    super();
    this.addEventListener("example-event", ({ detail }) => {
      console.log("I'm listening");
    });
  }
}
customElements.define("example-component", ExampleComponent);

And I'm using it on the index.astro page like this:

---
import ExampleComponent from "components path";
---
<div>
  <ExampleComponent></ExampleComponent>
</div>
<script>
  const exComponent = document.querySelector("ExampleComponent");
  const exEvent = new CustomEvent("example-event", {
    detail: "some data can go here"
  });
  exComponent.dispatchEvent(exEvent);
</script>
lofty wolf
#

I fear that my knowledge on web components is too limited for me to help. Maybe someone else has more experience with that.

My best guess is, that it is somehow related to the shadow DOM. AFAIK web components are rendered with a shadow DOM root node. Event propagation usually do not propagate to shadow dom childs (it stops at the shadow DOM root node). If that sounds like it could be related, thats where I would start.

merry trellis
#

No problem, thanks for all your help so far! I think it's time to go back to watching some youtube tutorials.

hybrid lotus
#

This line is not querying your element correctly js document.querySelector("ExampleComponent"); To target the first instance of your component you can use the tag name like this: js document.querySelector("example-componenet"); Or you can also use a class: ```jsx
<example-component class="my-component">
</example-component>
...
document.querySelector(".my-component");

queen onyx
#

Here you go:
custom-events.ts:

declare global {
    interface GlobalEventHandlersEventMap {
        'main-drawer:toggle': CustomEvent<{ open: boolean }>;
    }
}

export const createCustomEvent = <T extends keyof GlobalEventHandlersEventMap>(
    type: T,
    eventInitDict: CustomEventInit<
        GlobalEventHandlersEventMap[T] extends CustomEvent<infer T> ? T : never
    >,
) => new CustomEvent(type, eventInitDict);

Example usage:

<script>
    import { createCustomEvent } from "../lib/custom-events";

    const collapsible = document.querySelector<HTMLDetailsElement>('<your-element-selector>');

    // dispatch a custom event on some action (click, scroll events / other custom event)
    collapsible.addEventListener('toggle', function (event) {
        this.dispatchEvent(
            createCustomEvent('main-drawer:toggle', {
                // do not forget bubbling for global events (beyond `this` element)
                bubbles: true,
                detail: { open: collapsible.open },
            }),
        );
    });

    ... 
    
    // subscribe to custom event
    window.addEventListener('main-drawer:toggle', event => {
        console.log(event.detail.open);
    });
</script>
#

you should get autocompletion for your custom events