Create and Dispatch Synthetic Events

Alex Reardon
InstructorAlex Reardon

Share this video with your friends

Send Tweet
Published 6 months ago
Updated 5 months ago

In addition to the browser creating events, it is also possible for you to create and dispatch your own events.

Instructor: [0:00] The browser automatically creates and dispatches events in response to user and system actions. It is also possible for you to create and dispatch your own events. Manually created events are commonly referred to as, synthetic events. Creating an event is quite straightforward.

[0:17] Here I'm creating a new event by calling new and then the event constructor and parsing in my event name. You can dispatch events on event targets by using the dispatch-event function, which takes an event object as its only argument. On my button I've added an event list now for the hello event, which I've created here and I'm dispatching.

[0:38] In my on-hello function, I'm logging out the event to the console. If I come over here, I can see that I'm getting an event logging out. It's got the stuff in here that I roughly expect to see. When creating an event, we can use the event constructor parsing in the name of the event we want to create, as well as an optional event init object which has three optional properties.

[1:01] We have the bubbles Boolean property which controls whether or not the event will have a bubble phase and this defaults to false. We also have the cancelable Boolean property which dictates whether the event can be cancelled and this also defaults to false.

[1:16] We also have the composed Boolean property which dictates whether an event will travel through the boundaries of a web components shadow DOM and is also defaults to false. If you don't provide a value for these properties, then their default value will be used.

[1:32] If I come back to this usage where the button is logging out the hello event when it receives it, I change one of these properties to true. I'll dispatch the event and then I'll come over to the console and I can see that bubbles is set to true, which is what I expect.

[1:47] Cancelable is set to false which matches what we provided on the event init object. Composed is set to false which also matches what we provided. Some other things we can observe in this event object is that the target of the event is set to button.

[2:01] When we dispatch an event, the event.target property gets set to the event target that we're dispatching the event on. Also you'll see in here that the type of the event has been set to hello which matches this event type that we've parsed into our new event constructor.

[2:19] Different categories of events can have their own constructors. These different constructors support the three base event init properties, as well as event specific properties. Say, for example, the mouseEvent constructor also supports things like the button.

[2:36] If you want to see what properties you can parse into an event constructor, and what the default values of those properties will be, good place to start, again, is the MDN event reference. I'm going to search for the click event. I can see here it uses the mouse event interface. I'll go into here. I'll go into the mouse event constructor.

[3:00] I can see that I have to parse in type arg and mouse event init, which is this big object here. I can see all the properties I can parse in, as well as their default values.

[3:15] You can use the event constructor to create events of lots of different types. In this case, I'm creating a click event. This will, in fact, get picked up by this click event listener I added to the button. Over here, I can see that, yeah, that is getting picked up by this click event listener.

[3:30] However, you can't set any of the standard mouse event constructor properties, so if my onClick function was expecting event.button to be set, it's not being set there. If I come over here and parse in button0, that doesn't get parsed through.

[3:52] Here, I'm dispatching an event that's created with a new mouse event constructor, which will give all of the mouse event properties their default values. You can see that event.button is getting printed out at zero, which is its default value. It's always safest to use the appropriate event constructor for a given event. That way, event listeners will always be provided with the appropriate event object for that event type.

[4:17] If you want to create your own domain specific events, the first-class way of doing that is using the custom event constructor. The custom event constructor is almost identical to the events constructor, except it also lets you parse in a detail object property in the event init object.

[4:36] This detail property can be any value that you want, and in here, I'm parsing in an object with a property called hello, which has a value there. After I've dispatched the event, it's going to be logged out, and I come over to the console here, have a look in my object, and I can see that there is a detail property here with the value hello there.

[4:57] Dispatching a click event against our button, which participates in the DOM, will cause the event to travel down from the window in the capture phase towards the button, through the button in the target phase, and then back up to the window in the bubble phase. This particular click event will bubble, because I've set bubbles to true.

[5:18] To demonstrate that that is indeed happening, I've added a click event listener to the parent element, and I've added that event listener in the capture phase, as the event is going down towards the button. Inside of the onClick function, I'm going to log out the event target, which I expect to be the button element, which is the event target that we're dispatching our event against.

[5:41] If we come over here to the console, we'll see that our parent element was hit, so the event did go through the different phases, and the event.target property was set to the button element, which is what we expected.

[5:55] Now the onClick function is going to cancel the event by using event.preventDefault. How can we find out if this event was canceled? Because we have access to the event object, we can look at the event.defaultPrevented property to understand if an event was canceled. If we come up here, we'll see that canceled is false, which is not what we expected.

[6:17] We canceled the event using event.preventDefault, or at least we tried to. Now, we didn't actually cancel the event, because cancellable was set to false, because it was using the default value, so we've had to override that to say that, yes, we do actually want this click event to be cancellable, and so now our event.preventDefault is in fact canceling the event.

[6:39] Dispatch event also returns a Boolean value which represents where the event was canceled. A true value means that the event was allowed or not canceled, and a false value means that the event was canceled. Here, I can log out that the event was canceled by inverting the result of this Boolean.

[6:59] Some event targets, such as HTML element, have a first-class API for quickly creating and dispatching an event. Here, I'm calling button.click, and that is being picked up by the click event listener that was added to the parent element in the capture phase, and it's logging out the event over here in the console.

[7:20] This particular shortcut API is equivalent to calling button.dispatchEvent and parsing in a new click mouse event with bubble set to true, cancellable set to true, and compose set to true. These shortcut APIs do not give you control over the event that's being created.

[7:38] You cannot control the bubbles cancellable of composed properties or the event-specific properties such as button. These shortcut event creation and dispatch APIs are quick and easy, but they're also fairly limited in their flexibility and utility.

[7:57] I do find myself using these sometimes in tests, but more often than not, I find I'm reaching for something like this to have increased control over the event, and also to be super clear with other people what these properties are, because not everybody will know what the properties are on the event that's being created from these shortcut APIs, whereas this is easy to see what's going on.

[8:21] When manually creating events, the event.isTrusted property on the event will be set to false. This allows event listeners to know if the event was created by the browser or if it is synthetic. Here, I am adding a click event listener to the button, and I'm going to log out whether the event is trusted, and then creating a new click mouse event and dispatching that event on the button.

[8:42] If I come up to the browser and I refresh the page, we'll see that this created event was not trusted, and if I click on the button, we'll see that these click events are trusted. That's because these events are being created by the browser.

[8:54] As I've mentioned previously, the event.timestamp property is set when the event is created, and not when it's dispatched. Now that we understand more about event creation and dispatch, I thought I'd prove that to you. Here, I'm adding a click event listener to the button, and it's going to log out timestamp inside the listener and it's going to log out event.timestamp.

[9:19] I'm creating a new click mouse event. I'm then going to log out the timestamp property, saying this is what's the initial timestamp, and then I'm going to use setTimeout to queue a task to happen after 2,000 milliseconds. In that task, it's going to dispatch my click event.

[9:39] If we come over to the browser and I refresh the page, we'll see we get timestamp initial logged out is 41, and then after 2,000 milliseconds, we had timestamp listener, which was this call up here logging out the timestamp, which was also 41.5 milliseconds. This clearly shows that the event.timestamp property is set and locked in when the event is created and not when it's dispatched.

[9:20] I'm adding a click event listener to the button and logging out the event, creating a new click MouseEvent, and dispatching it. I can dispatch the exact same event multiple times if I want to. There's nothing stopping me from doing that. That's all good.

[10:24] When an event is dispatched its internal stop propagation and stop immediate propagation flags are cleared, but the internal canceled flag is not cleared. Once an event has been canceled, it stays canceled, and dispatching that event multiple times doesn't change that.

[10:40] I've personally never seen a use for dispatching the same event multiple times. Personally, if I need to dispatch an event multiple times, I'll create a new event for each dispatchEvent() call.

[10:52] Most SyntheticEvents do not trigger the default browser actions associated with the event type. The exception to this rule is synthetic click events, which will trigger the default behavior for a click event.

[11:05] I've created an anchor on the page which has a href set to DOM events.Dev, and then grabbing that anchor using query selector. Inside of my button onClick function, I'm going to call anchor.click.

[11:18] The default click behavior on an anchor used to navigate to the href. Clicking on the button will cause a synthetic click event to be fired on the anchor which should cause the page to navigate. There we go.

[11:32] For complete clarity, this behavior is exactly the same regardless of how you create the click event. Here, rather than using the shortcut API, I'm using dispatchEvent() and parsing in a new click MouseEvent. When I click on the button now, we see the exact same behavior.