The Event Object

Alex Reardon
InstructorAlex Reardon
Share this video with your friends

Social Share Links

Send Tweet
Published 3 years ago
Updated 3 years ago

Event listeners are provided with an event object that represents an actively dispatched event. An event object contains lots of information and functionality related to an event. We will look at the various event properties, constants and methods you have at your disposal on an event object and the Event constructor.

Instructor: [0:00] Event handlers and event listeners are provided with an event object. Every event object gives you a lot of information about an event as well as mechanisms to impact the event itself. Firstly, the event constructor has four constants. These represent the different states that an event can be in.

[0:19] Event.none is when the event has not yet been dispatched or has finished dispatching. We have capturing phase which is when the event is in the capture phase, at target which is when we're in the target phase, and bubbling phase when we're in the bubbling phase. These properties map to integers, which I've logged out here in the console, so zero, one, two and three.

[0:39] Taking a look at properties on the event object, firstly, we have the event phase which matches the phase that the event is currently in. Our eventListener is added to the parent in the capture phase. When we click the button, we should expect this value to be capture. It's logging out at one, which isn't all that easy to work with. Here, I have created a little object called map, which just maps the event constructor constants to a readable string.

[1:07] In my onclick function, I'm now no longer going to log out the event phase directly, but I'm going to use that to look up a more readable name. Now when I click the button, I'll see that the nice string capture is being printed out, and if I change my capture value to false, I'm now registering this event listener in the bubble phase, so when I click the button, I'll expect to see bubble printed here.

[1:30] Another property on the event object is type, which matches the event type that the event listener was added for. When I click my button, I'll see that my event type is click, which matches what I've got here.

[1:42] The next two properties we'll look at is target and currentTarget. These two properties are super useful. Event.target points at the event target in which the event is occurring and where the event is sort of traveling to. Event.currentTarget matches the event target that this event listener was added to. Here, I'm adding an event listener to the parent element.

[2:06] If I were to click on the button, I should expect to see event.target to be button, and my current target property should point to parent, because that's what this event listener will be bound to. When I click on my button, I see that yes, the target is the button, and the current target is the parent.

[2:25] If I click on the parent element rather than on the button, I'll see that target is now set to parent, and currentTarget remains pointing at parent as well, because that is the element that the event listener was bound to. Next up we have the three event innate properties, bubbles, cancelable and composed.

[2:43] Bubbles is a Boolean that will be set to true if the event will have a bubble phase and will be set to false if it won't have a bubble phase. Cancelable is another Boolean property which will be set to true if the event can be cancelled and false if the event cannot be cancelled.

[2:58] I'll cover cancelling events in the cancelling events lesson. We have the composed property which is a Boolean value that reflects whether an event will travel through the boundaries of a web component shadow DOM.

[3:12] If you're not working with web components, then this property isn't all that useful. The isTrusted property of an event is a Boolean which reveals whether the event was created by the browser itself or whether it was manually created by another piece of code.

[3:28] If I click on the button, I'll say that isTrusted is set to true and that's because this event is being created by the browser. Now in my code I'm going to call button.click which is a shortcut method for creating and dispatching a click event.

[3:41] We can see over here that now our onclick function is logging out isTrusted false because this event was manually created and dispatched. I will talk about event creation and dispatch in more detail in my synthetic events lesson.

[3:56] Let's have a timestamp property which represents the time in milliseconds that the event was created. This value is relative to the time origin of the page so it will continue to grow bigger and bigger as the page gets older and older.

[4:11] For events that are created by the browser, the creation time and dispatch time of the event are the same. However, for manually created events, the creation of an event and the dispatching of that event might be separated by quite a long period of time.

[4:26] The timestamp property is for when the event is created not when it's dispatched. Events also have a default prevented property which will be set to true when an event is cancelled.

[4:38] The misleadingly named return value property of an event also represents whether an event was cancelled. However, return value is set to false when an event is canceled and true when an event is not cancelled which is the inverse of default prevented.

[4:56] If I click my button, now this event is not being cancelled, so I'll say that default prevented is set to false and return value is set to true. I do talk more about return value and why it's misleadingly named in an upcoming lesson, but for now just keep in mind that this property is not something you want to really be looking at or interacting with.

[5:16] Every event has a single shared event object. Every HTML attribute event handler, element property event handler, and event listener on the event path will be provided with an event object that shares the exact same reference.The shared event object will mutate as it flows through event listeners and event targets, as well as in response to side effects such as canceling an event.

[5:46] Here, I'm adding a clickEventListener to the parent element. Inside of my onclick function, I'm going to log out the event.currentTarget which points to the event target that the event listener is bound to, which in this case should be the parent element. You can see over here it's pointing to parent.

[6:04] Now using setTimeout, I'm going to cue a task to be run at a later point. I'm again going to try and log out event.currentTarget. When I click the button, I can see that when my event handler was called, the event.currentTarget property was set to parent, but at this later point in the future, the event.currentTarget property was null.

[6:25] The currentTarget property changes as the event flows through the different event listeners. It's also set to null when the event is finished dispatching. I have seen this type of gotcha come up a few times, and the root cause is that this event object is mutating.

[6:41] If you're going to hold on to that event object and use it at a later point, it may have some values in there that you didn't expect. If you do need to refer to an event property in a later task, I do recommend capturing that value in a local variable.

[6:55] In this case, I'm capturing the event.currentTarget property inside a constant called captured. Then in my future task, I'm going to refer to that captured value and use that as a pointer to what the event.currentTarget was when this eventListener was here.

[7:10] Now, when I click the button, I'll see that event.currentTarget was set to parent when the eventListener was called. After the setTimeout in a future task, while the event.currentTarget property is set to null because I captured that current target in my captured constant, I'm still able to get access to this value.

[7:30] Event objects also have a number of methods that you can use. The first one is composedPath, which returns an array of event targets that an event will travel through during an event. The returned array starts with the target of the event and then works up the tree of event targets.

[7:48] When I click on the button to see the composedPath, I will see that the event started with button. This is in the target phase. Then it's going to go through the bubble phase up to the child, the parent, body, HTML element, document, and finally the window. There doesn't need to be any event listeners on an eventTarget for to be included in the event path.

[8:12] The composedPath function or return the same event path regardless of which event target the eventListener is added to. In this case, my eventListener is added to the parent element, but the target of the event was the button. The composedPath function will only return the event path when the event is being dispatched. Once the event has finished or if it hasn't been dispatched yet, then composePath will just return an empty array.

[8:39] Another event object method is preventDefault, which will cancel an event if it is cancelable. I will talk more about preventDefault in my Canceling Events Lesson. It's also possible for an eventListener to stop an event on the event path using the stopPropagation and stopImmediatePropagation functions. I'll cover these functions and their behavior in more detail in the Stopping Events Lesson.

[9:04] In addition to the base event properties, different event types add their own additional properties, constants, and methods to the event object. Here, I'm adding a clickEventListener. I'm going to log that event out to the console.

[9:19] When I click my button, I will see that this was a mouseEvent as a whole lot of properties in here relating to that mouseEvent, so things like the button, the clientX position, and the clientY position. If you want to find out what things you have available to you for an event, a great place to start is the MDN Event Reference.

[9:38] Here, I'm going to search for the clickEvent. I'll go in here, and I can see that it uses the mouseEvent interface. If I click in here, here I'm presented with information about mouse events. I can scroll down to see all the different properties I have available, constants and methods.

~ 2 years ago

Hey Alex,

I have two questions:

You introduce the event object including the currentTarget property. At first glance, we can use either this or event.currentTarget in our handler function to achieve the same result:

const button = document.querySelector("button"); function listener(event) { console.log("this: ", this); console.log("event.currentTarget: ", event.currentTarget); } button.addEventListener("click", listener, { capture: false, });

Now, you discussed the gotchas with using this, but is there any rationale of choosing one over the other for most cases if we aware of said gotchas?

For my second question: From the componentPath example we can see that the DOM stucture is as follows: button > div.parent > div.child > button

If I change div.parent to p.parent with everything else being equal our listener will not trigger when interacting with div.child or button any longer. What is the determining factor when it comes to an event traversing the DOM based on the HTML element type?

Best regards, David

Alex Reardon
Alex Reardoninstructor
~ 2 years ago

Hi David,

Thanks for reaching out. Let's dive into your questions.

Now, you discussed the gotchas with using this, but is there any rationale of choosing one over the other for most cases if we aware of said gotchas?

You are completely free to choose whatever approach you like. I personally find myself avoiding using this as it can be confusing for some folks and is subject to a few gotchas. Using this means you need to be aware of where the function is being run as this is determined by the functions call site.

If I change div.parent to p.parent with everything else being equal our listener will not trigger when interacting with div.child or button any longer.

I think you will find the answer to this one if you open your dev tools. You will see that by changing .parent from a div to a p, your .parent element is no longer a parent element. This is because the p.parent will be closed automatically and not allow block element children.

From MDN:

"Paragraphs are block-level elements, and notably will automatically close if another block-level element is parsed before the closing </p> tag. See "Tag omission" below."

What is the determining factor when it comes to an event traversing the DOM based on the HTML element type?

event.composedPath() returns all the parent EventTargets from the target of the event (event.target) up to the root EventTarget (the window). All Elements are EventTargets so any parent element of the target of the event (event.target) will be included in event.composedPath().

Any more questions, let me know.

Cheers

~ 2 years ago

Thanks for elaborating Alex!

I find the explanation of MDN to be a bit odd though:

"Paragraphs are block-level elements,..." divs are also block-level elements. The phrasing suggests that being a block-level element is of importance here but it seems only the Tag omission is really relevant.

Alex Reardon
Alex Reardoninstructor
~ 2 years ago

I think they are trying to call out two things:

  • p is a block element (similar to div)
  • p will automatically close if a child of p is found to be a block element (the tag closing behaviour)

I recently came across this video which explores some of the strangeness of the p tag: Magic tricks with the HTML parser

Markdown supported.
Become a member to join the discussionEnroll Today