javascripteventsweb-component

Is it possible to use the onEventName pattern/syntax for custom events in my own class?


I've written my own JavaScript class (for a custom web component). It raises a couple of events. For instance: TagChanged

To listen to this event, I (obviously) do this:

myObject.addEventListener('TagChanged'), (e) => doSomething());

But is it possible to have something like:

myObject.onTagChanged = (e) => doSomething();

The default DOM elements have things like onClick and onKeyDown. Are they using some other architecture?


Solution

  • default Events onclick and onkeydown. Are they using some other architecture?

    Yes, Browser code us mortals can not access

    But the Browser also calls a handleEvent method on a JavaScript class... if a mortal created it

    From MDN - add EventListener

    The method addEventListener() works by adding a function, or an object that implements a handleEvent() function, to the list of event listeners for the specified event type on the EventTarget on which it's called. If the function or object is already in the list of event listeners for this target, the function or object is not added a second time.

    So you can do

    this.addEventListener("myEvent", this) // calls 'handleEvent' on 'this' class
    

    which (by default) calls handleEvent IF you created it!

    handleEvent(evt) { // handleEvent called by default 
      // check if "event_myEvent" method exists on class
      // then call as function(evt)
      this[ "event_"+evt.type ]?.(evt) 
    }
    

    Then triggers your method:

    event_myEvent(evt) {
      // your event handling
    }
    

    Full code

    Note: Events from shadowDOM require bubbles:true, composed:true to "escape" shadowRoots

    customElements.define("handle-event", class extends HTMLElement {
      handleEvent(evt) { // handleEvent is called by default on every JavaScript Object!
        this["event_" + evt.type]?.(evt) // check if "event_NNN" method exists
      }
      event_myEvent(evt) {
        let div = evt.composedPath()[0] // Event from DIV escaped shadowRoot
        div.colors.push(div.style.background = div.colors.shift());
      }
      connectedCallback() {
        this.addEventListener("myEvent", this) // calls 'handleEvent' on 'this' Object/Class by default
    
        const createElement = (tag, props = {}) => Object.assign(document.createElement(tag), props)
        const newDIV = (name) => {
          const divEvent = new CustomEvent("myEvent", {
            bubbles: true,
            composed: true, // listener is on <handle-event> not on shadowRoot, needs to escape shadowRoot
            detail: {},
          })
          const div = createElement("div", {
            textContent: name,
            colors : ["red","yellow","blue"],
            onclick: (evt) => div.dispatchEvent(divEvent),
          })
          return div;
        }
    
        this.attachShadow({mode:"open"}).append(
          createElement("style", {
            textContent: `div { cursor:pointer; background:teal; color:black; padding:10px }`,
          }),
          ...["FOO","BAR","BAZ"].map(newDIV)
        )
      }
    })
    Click row to cycle colors
    <handle-event></handle-event>