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.
1dispatch(name: string, detail?: Record<string, unknown>): voidThis 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:
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.
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:
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:
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:
1this.dispatch('countchange', { count: nextCount })If you need custom event options such as bubbles, composed, or cancelable, use the native API directly:
1this.dispatchEvent(2 new CustomEvent('countchange', {3 detail: { count: nextCount },4 bubbles: true,5 composed: true,6 })7)