Events in Web Components — Complete Guide
In this tutorial, you will learn about Events in Web Components. We cover key concepts, practical examples, and best practices to help you master this topic.
Events in Web Components use CustomEvent for communication, with retargeting across shadow boundaries and composed events for cross-boundary propagation.
What You'll Learn
- How to dispatch custom events from Web Components
- How events are retargeted across shadow boundaries
- The composed option for cross-boundary events
- How to listen for events on custom elements
Why It Matters
Event communication is how components notify the outside world about user interactions, state changes, and completed actions.
flowchart LR A[Component] --> B[dispatchEvent] B --> C[CustomEvent] C --> D[detail: data] C --> E[bubbles: true] C --> F[composed: true] D --> G[Parent receives event]
Dispatching Events
class ToggleSwitch extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this._on = this.hasAttribute('on');
this.shadowRoot.innerHTML = `
<style>
.track { width: 44px; height: 24px; background: #ccc; border-radius: 12px; cursor: pointer; position: relative; transition: background 0.3s; }
.thumb { width: 20px; height: 20px; background: white; border-radius: 50%; position: absolute; top: 2px; left: 2px; transition: left 0.3s; }
:host([on]) .track { background: #2ecc71; }
:host([on]) .thumb { left: 22px; }
</style>
<div class="track" part="track"><div class="thumb" part="thumb"></div></div>
`;
this.shadowRoot.querySelector('.track')
.addEventListener('click', () => this.toggle());
}
toggle() {
this._on = !this._on;
if (this._on) {
this.setAttribute('on', '');
} else {
this.removeAttribute('on');
}
this.dispatchEvent(new CustomEvent('toggle', {
detail: { on: this._on },
bubbles: true,
composed: true
}));
}
get on() { return this._on; }
}
customElements.define('toggle-switch', ToggleSwitch);
Event Retargeting
Shadow Dom retargets events so the host element appears as the target, not internal nodes.
// JavaScript
document.querySelector('toggle-switch').addEventListener('toggle', (e) => {
console.log('Toggle event:', e.detail);
console.log('Target:', e.target); // The <toggle-switch> element, not internal div
console.log('Current target:', e.currentTarget);
});
Common Mistakes
- Forgetting to set composed: true on events that need to cross shadow boundaries
- Not setting bubbles: true for ancestor listening
- Dispatching events on internal elements instead of the host (this)
Practice Questions
- What does the composed option do? Allows the event to propagate across shadow DOM boundaries into the main DOM.
- How does event retargeting affect event.target? The target is changed to the host element instead of the internal shadow element.
Mini Project
Build a form component with multiple input child components. Each input dispatches a field-change event. The parent form listens and validates. Submit dispatches a form-submit event with all field values.
What's Next
Lesson 14: Form Participation
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro