Events

Custom elements often need to communicate changes or actions back to their parents or the rest of the application. @beforesemicolon/web-component provides a built-in helper method, this.dispatch(), to dispatch custom DOM events cleanly.

Dispatching Custom Events

To dispatch a custom event from inside a component, use the this.dispatch(name, detail) method.

typescript
1dispatch(name: string, detail?: Record<string, unknown>): void

This method is a convenient wrapper around the native dispatchEvent API. It instantiates a standard CustomEvent and passes the second argument as event.detail.

Here is a practical example of a counter component that dispatches an event whenever the count changes:

javascript
1import { WebComponent, html } from '@beforesemicolon/web-component'2 3class CounterButton extends WebComponent {4    initialState = { count: 0 }5 6    increment = () => {7        const nextCount = this.state.count() + 18        this.setState({ count: nextCount })9 10        // Dispatch the custom event with the payload11        this.dispatch('countchange', { count: nextCount })12    }13 14    render() {15        return html`16            <button type="button" onclick="${this.increment}">17                Increment (${() => this.state.count()})18            </button>19        `20    }21}22 23customElements.define('counter-button', CounterButton)

Passing Payloads

The second parameter to this.dispatch is an optional detail object containing any custom data. In the receiving event listener, this payload is available via event.detail.

javascript
1// Inside your component2this.dispatch('submit', {3    userId: 'usr_123',4    timestamp: Date.now(),5})

Listening to Custom Events

Since components are native custom elements, you can listen to these events using standard web APIs or directly inside Markup templates.

In Markup Templates

Markup templates support binding event listeners for any standard or custom event by prefixing the event name with on.

For example, if your custom element is named <counter-button> and dispatches a countchange event, you can listen to it in a template like this:

javascript
1import { html } from '@beforesemicolon/web-component'2 3const handleCountChange = (event) => {4    console.log('New count received:', event.detail.count)5}6 7const template = html`8    <div>9        <h3>My Application</h3>10        <counter-button oncountchange="${handleCountChange}"></counter-button>11    </div>12`

Using Native Event Listeners

You can also interact with the component imperatively in standard JavaScript using the native addEventListener method:

javascript
1const element = document.querySelector('counter-button')2 3element.addEventListener('countchange', (event) => {4    console.log('Count updated imperatively:', event.detail.count)5})

Event Boundary Notes

this.dispatch() intentionally keeps a small API:

javascript
1this.dispatch('countchange', { count: nextCount })

If you need custom event options such as bubbles, composed, or cancelable, use the native API directly:

javascript
1this.dispatchEvent(2    new CustomEvent('countchange', {3        detail: { count: nextCount },4        bubbles: true,5        composed: true,6    })7)
edit this doc