Dispatching Custom Events

Custom events are used for generating graphical components. It can create not only completely new events, but also built-in events such as mousedown, click, and so on. It can be handy for automated testing.

Event Constructor

Built-in event classes are similar to DOM element classes: they set up a hierarchy. The built-in Event class is the root.

Event objects are generated in the following way:

let event = new Event(type[, options]);

Two arguments are distinguished: type and options. The type argument is the event type. The options argument is the object that has two optional properties such as:

  • bubbles: true/false: in case it’s true, the event will bubble.
  • cancelable: true/false: in case it’s true, then the default action can be prevented.

However, both of them are false ({bubbles: false, cancelable: false}) by default.

Dispatch Event

Once an event object is generated, it should be run on an element applying the elem.dispatchEvent(event) call.

The handlers respond to it, as it was an ordinary browser event. If you have created the event using the bubbles flag, then it will bubble.

Let’s check out an example where the click event is created in JavaScript:

<!DOCTYPE html> 
<html>
  <head> </head>
  <body>
    <button id="buttonId" onclick="alert('Clicked');">Autoclick</button>
    <script>
      let event = new Event("click");
      buttonId.dispatchEvent(event);
    </script>	
  </body>
</html>

Bubbling Example

A bubbling event can be created with the name "hello".

First of all, it’s necessary to set bubbles to true, like this:

<!DOCTYPE html> 
<html>
  <head> </head>
  <body>
    <h1 id="elemId">Welcome to W3Docs</h1>
    <script>
      // catch on document
      document.addEventListener("hello", function(event) { 
        alert("Hello from " + event.target.tagName); // Hello from H1
      });
      // dispatch on elem
      let event = new Event("hello", {bubbles: true});
      elemId.dispatchEvent(event); 
      // the document handler activates and displays a message
    </script>
  </body>
</html>

MouseEvent, KeyboardEvent and More

Let’s explore the shortlist of the classes for UI events:

  1. UIEvent
  2. FocusEvent
  3. MouseEvent
  4. WheelEvent
  5. KeyboardEvent

They can be used instead of new Event for creating such events. For example, new MouseEvent("click").

The correct constructor helps to indicate standard properties for such an event. For example, clientX/clientY for a mouse event:

let event = new MouseEvent("click", {
  bubbles: true,
  cancelable: true,
  clientX: 200,
  clientY: 200
});
console.log(event.clientX); // 200

Please, consider that the generic Event constructor doesn’t accept that. Here is what will happen if you try it:

let event = new Event("click", {
  bubbles: true, // only bubbles and cancelable
  cancelable: true, // work in Event constructor
  clientX: 200,
  clientY: 200
});
console.log(event.clientX); // undefined, unknown property is ignored

Custom Events

Custom events are technically almost the same as Event. But, there is an exception.

Let’s start at an example where, in the second argument, an additional detail argument is added for custom information:

<!DOCTYPE html> 
<html>
  <head> </head>
  <body>
    <h1 id="elemId">Welcome to W3Docs</h1>
    <script>
      // add details come with event to the handler
      elemId.addEventListener("welcome", function(event) {
        alert(event.detail.name);
      });
      elemId.dispatchEvent(new CustomEvent("welcome", {
        detail: { name: "W3Docs" }
      }));
    </script>
  </body>
</html>

The detail property may include any data.

The CustomEvent provides specific detail field for it for avoiding conflicts with other event properties.

Events-in-events are Synchronous

As a rule, events consider an asynchronous process. In other words, the browser processes onclick, and then a new event triggers. Then it waits till the onclick processing is over.

But, there is an exception: when an event is initiated within another one. Afterward, the control passes to the nested event handler, then goes back.

Here is an example:

<!DOCTYPE html> 
<html>
  <head> </head>
  <body>
    <button id="menu">Click on button</button>
    <script>
      menu.onclick = function() {
        alert("one");    // alert("nested")
        menu.dispatchEvent(new CustomEvent("menuOpen", {
          bubbles: true
        }));
        alert("two");
      };
      document.addEventListener('menuOpen', () => alert('nested'));
    </script>
  </body>
</html>

In the example above, the nested event menu menuOpen bubbles up, and then it is being handled on the document. The propagation and handling of the nested event should be completed before the processing returns from the outer code (onclick). It’s not only for dispatchEvent but for different other cases, too. Also, it is possible to place the dispatchEvent at the end of onclick or to wrap in setTimeout, like this:

<!DOCTYPE html> 
<html>
  <head> </head>
  <body>
    <button id="buttonId">Click on button</button>
    <script>
      buttonId.onclick = function() {
        alert("one"); 
        // alert(2)
        setTimeout(() => buttonId.dispatchEvent(new CustomEvent("menuOpen", {
          bubbles: true
        })));
        alert("two");
      };
      document.addEventListener('menuOpen', () => alert('nested'));
    </script>
  </body>
</html>

As you can see, dispatchEvent is executed asynchronously after the current code running is over. So, event handlers are completely separate.

The output sequence will be as follows:

one → two → nested

Summary

For generating an event from code, first of all, an event object should be created.

The CustomEvent constructor is used for custom events. It includes an extra option, called detail, to which the event-specific data is assigned. Afterward, all the handlers will have access to it as event.detail .

Custom events with their own names are, generally, created to meet architectural goals, for signaling what happens inside menus, carousels, sliders, and so on.




Do you find this helpful?

Related articles