Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Creating Truly Custom Events for Web Components
Burton Smith
Burton Smith

Posted on

     

Creating Truly Custom Events for Web Components

When building web components, communication between components and their parent applications is crucial. While the built-in DOM events likeclick andchange work well for standard interactions, custom events give you the power to create meaningful, semantic event types that are tailored for your components.

In this post, we'll explore how to extend JavaScript'sEvent class to create custom events that are both powerful and maintainable.

Why Extend the Event Class?

Before diving into the how, let's understand the why we would want to extend theEvent class. It allows you to:

  • Create events with custom properties and methods
  • Implement type-safe event handling
  • Create reusable event classes across multiple components
  • Add meaningful default options for events

If you decide that creating custom event subclasses is not the approach you want to take, you canstrongly type your events to create a good developer experience.

Basic Custom Event Extension

Let's start with a simple example. Here's how you might create a custom event for a shopping cart component:

classCartItemAddedEventextendsEvent{// strongly type the event targetdeclaretarget:ShoppingCart;// define the parameters of the event using the constructorconstructor(item,quantity=1){// pass the event name and any options to the base Event classsuper('cart-item-added',{bubbles:true,cancelable:true,composed:true});// initialize public property valuesthis.item=item;this.quantity=quantity;this.timestamp=newDate();}/** Get total price for added item */gettotalPrice():number{returnthis.item.price*this.quantity;}}// Usage in a web componentclassShoppingCartextendsHTMLElement{addItem(item,quantity){// Add item to cart logic here...// Dispatch custom eventconstevent=newCartItemAddedEvent(item,quantity);this.dispatchEvent(event);}}
Enter fullscreen modeExit fullscreen mode

Documenting Events

To document these custom events, we can use the@event or@fires JSDoc tags in the component class's documentation. In the description, we can also provide a type. This information will all be added to theCustom Element's Manifest. This is very helpful when creating custom integrations and type definitions for various environments.

/** * ... other documentation * * @event {CartItemAddedEvent} cart-item-added - emitted when an item is added to the cart */classShoppingCartextendsHTMLElement{...}
Enter fullscreen modeExit fullscreen mode

Global Event Map Registration

To enable automatic type inference withaddEventListener, register your custom events in the global event map:

declareglobal{interfaceGlobalEventHandlersEventMap{'cart-item-added':CartItemAddedEvent;}}
Enter fullscreen modeExit fullscreen mode

Advanced Custom Event with Validation

Here's a more sophisticated example of what can be done with custom events that includes validation and error handling:

typeValidationResult={isValid:boolean;errors:string[];};classFormValidationEventextendsEvent{// strongly type the event targetdeclaretarget:CustomForm;constructor(fieldName:string,value:string,validationResult:ValidationResult){super('form-validation',{bubbles:true,cancelable:false,composed:true});this.fieldName=fieldName;this.value=value;this.validationResult=validationResult;this.isValid=validationResult.isValid;this.errors=validationResult.errors||[];}staticcreateSuccess(fieldName,value):FormValidationEvent{returnnewFormValidationEvent(fieldName,value,{isValid:true,errors:[]});}staticcreateError(fieldName,value,errors):FormValidationEvent{returnnewFormValidationEvent(fieldName,value,{isValid:false,errors:Array.isArray(errors)?errors:[errors]});}hasError(errorType:'tooLong'|'invalidType'|'required'):boolean{returnthis.errors.some(error=>error.type===errorType);}getErrorMessage():string{returnthis.errors.map(error=>error.message).join(',');}}// Usage in a form componentclassCustomFormextendsHTMLElement{validateField(fieldName,value){constvalidationResult=this.runValidation(fieldName,value);// using static methods to instantiate specific event variationsconstevent=validationResult.isValid?FormValidationEvent.createSuccess(fieldName,value):FormValidationEvent.createError(fieldName,value,validationResult.errors);this.dispatchEvent(event);}}
Enter fullscreen modeExit fullscreen mode

Creating Event Hierarchies

You can extend your custom events for complex component systems and set meaningful defaults in base classes to reduce repetitive code:

// Base event classclassComponentEventextendsEvent{constructor(type,options={}){super(type,{bubbles:true,cancelable:true,composed:true,...options});this.timestamp=newDate();this.componentId=options.componentId||null;}}// Specific event typesclassComponentLoadedEventextendsComponentEvent{constructor(componentId,loadTime){super('component-loaded',{componentId});this.loadTime=loadTime;}}classComponentErrorEventextendsComponentEvent{constructor(componentId,error){super('component-error',{componentId});this.error=error;this.severity=error.severity||'error';}getisCritical(){returnthis.severity==='critical';}}
Enter fullscreen modeExit fullscreen mode

Pros and Cons of Subclassing Events

Like any engineering decision, there are trade-offs with choosing to go with a custom event class or using a standardEvent orCustomEvent. Here are some pros and cons to subclassingEvent that teams should take into consideration when making a decision.

Pros of Extending the Event Class

Type Safety and IntelliSense

When using TypeScript or modern IDEs, extended Event classes provide excellent autocomplete and type checking without having to create new types.

Reusability

Event classes can be imported and reused across multiple components, ensuring consistency and uniformity.

Custom Logic

You can add custom logic to your events rather than repeating it in your components.

Rich API

Custom methods and getters on your event classes provide a clean API for event consumers.

Debugging

Custom event classes make debugging easier with meaningful class names in stack traces.

Event Instance Checking

Because the events are a specific class, developers can validate the event type by usinginstanceof. This is very helpful if you are using event delegation by listening to events on parent elements.

someDiv.addEventListener('my-event',e=>{if(einstanceofMyCustomEvent){// logic for handling the event}});
Enter fullscreen modeExit fullscreen mode

Cons of Extending the Event Class

Bundle Size

Each custom event class adds to your JavaScript bundle size (though the impact is usually minimal).

Learning Curve

Developers need to understand your custom event system, which adds complexity compared to standard DOM events.

Event Property Pollution

When creating properties and methods on a custom event, those will be collocated with the standardEvent properties and methods. Teams may prefer having custom data located in a standard interface like thedetail property of aCustomEvent.

Potential Overengineering

It's easy to create overly complex event hierarchies that don't provide sufficient value.

Browser Compatibility

While modern browsers support class extensions well, older browsers may encounter issues (although this is rarely a concern for modern web components in evergreen browsers).

Memory Overhead

Creating many custom event instances can use more memory than simple object literals (though this is typically negligible).

Difficult to Create Abstractions

If you are creating utilities for emitting events, it can be difficult since events will need to instantiate a custom event class. For example, a common approach with web component libraries is to create anemit utility method in a component base class that can easily be used in inherited classes.

classBaseComponentextendsHTMLElement{emit<T=unknown>(name:string,detail?:T){this.dispatchEvent(newCustomEvent<T>(name,{detail,bubbles:true,composed:true}));}}typeCustomClickDetails={message:string;};classCustomButtonextendsBaseComponent{constructor(){super();this.attachShadow({mode:'open'});}connectedCallback(){constbutton=document.createElement('button');button.textContent='Click Me';// emit a custom event when the button is clickedbutton.addEventListener('click',()=>{this.emit<CustomClickDetails>('custom-click',{message:'Button was clicked!'});});this.shadowRoot?.appendChild(button);// emit a custom event when the component has loadedthis.emit('loaded');}}
Enter fullscreen modeExit fullscreen mode

Things to Keep in Mind

  • Keep it simple. Don't create custom events unless they add real value overCustomEvent orEvent.
  • Include relevant data. Add properties that event listeners will need.
  • Don't repeat properties and methods that the user can get from the eventtarget.
  • Provide meaningful default options for your events.
  • Document your events. Provide clear documentation about when events fire and what data they contain.

Conclusion

Extending the JavaScript Event class is a powerful technique for creating rich, semantic event systems in web components. While it adds some complexity, the benefits of type safety, reusability, and clear APIs can outweigh the costs, especially in larger applications.

Use this technique judiciously - not every interaction requires a custom event class. However, when you do need rich event data and behavior, extendingEvent provides a clean and maintainable solution.

If subclassingEvent looks like a good fit for you, start with simple extensions and gradually build more sophisticated event systems as your component library grows.

Top comments(6)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
maxart2501 profile image
Massimo Artizzu
Senior web developer 🔥 ~ conf speaker 🎙️ ~ loves science 🔭, art 🎨, rugby 🏉 ~ reinventing a better wheel 🎡
  • Location
    Italy
  • Education
    Mathematics
  • Work
    Consultant at Antreem
  • Joined

Extending the nativeEvent class has been the way to go for me for a while. When it wasn't possible (up to ES5, actually),CustomEvent was the way to go, but now I think your own class relates more closely to what happen in other native even classes.

CollapseExpand
 
stuffbreaker profile image
Burton Smith
I am a senior software engineer at Microsoft and creator of the Web Components Toolkit (wc-toolkit.com). I love web components, web development, and design systems.
  • Location
    Columbus, OH
  • Education
    University of Nevada, Reno
  • Work
    Senior Software Engineer
  • Joined

I really like the idea of creating an additional layer of customer value in custom events.

CollapseExpand
 
dotallio profile image
Dotallio
  • Joined

Great breakdown of the trade-offs, especially around type safety vs. simplicity. Do you find yourself reaching for subclassed events or sticking to CustomEvent in your bigger projects?

CollapseExpand
 
stuffbreaker profile image
Burton Smith
I am a senior software engineer at Microsoft and creator of the Web Components Toolkit (wc-toolkit.com). I love web components, web development, and design systems.
  • Location
    Columbus, OH
  • Education
    University of Nevada, Reno
  • Work
    Senior Software Engineer
  • Joined

Honestly, I would say about 95% of the events in our component library do not have a payload, and the other 5% can probably be removed since the information is available on the event target (the custom element itself).

I think there are opportunities to create some nice dev experiences with extended events, but I'm not sure I'll do it for every event.

CollapseExpand
 
neurabot profile image
Neurabot
Longlife learner. Fall in love with oriented-programming C++/Python. Leading many projects in fields.
  • Location
    Cameroon
  • Pronouns
    He
  • Work
    CEO at valone
  • Joined

Cool. Great post.

When updating code of website files in management files cpanel, I have a problem.

I don't easily automatically update them in browser. I must do it 1 time. After it, i must clean browser to show another upgrade of code of files. How should I easily update browser to easily program in internet ?

CollapseExpand
 
stuffbreaker profile image
Burton Smith
I am a senior software engineer at Microsoft and creator of the Web Components Toolkit (wc-toolkit.com). I love web components, web development, and design systems.
  • Location
    Columbus, OH
  • Education
    University of Nevada, Reno
  • Work
    Senior Software Engineer
  • Joined

I'm not sure exactly what you're asking about here and how it relates to this article.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I am a senior software engineer at Microsoft and creator of the Web Components Toolkit (wc-toolkit.com). I love web components, web development, and design systems.
  • Location
    Columbus, OH
  • Education
    University of Nevada, Reno
  • Work
    Senior Software Engineer
  • Joined

More fromBurton Smith

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp