In this lesson, we explore how you can add event listeners to an event target with
addEventListener. It will also explain how you can control aspects of your event listener binding; as well as event listener equality.
Instructor: [0:00] Event targets such as elements have an addEventListener API, which allows you to add multiple event listeners to a single event.target, as well as giving you a lot of control over the event listener binding. Event listeners created with addEventListener are also known as DOM2 event listeners or DOM2+ event listeners.
[0:24] There are three arguments to the addEventListener function. The first argument is a string, which is the type of event you are going to be binding to. For example, click. The second argument is your event listener function or object. There is also an optional third argument, which gives you more control over the binding.
[0:45] When adding an event listener with the addEventListener API, the event listener will be added in the bubble phase by default, which is the same as HTML attribute event handlers and object property event handlers. The second argument to addEventListener can either be a function or an object that implements the eventListener interface. That is an object with a handleEvent function property.
[1:14] Here, I am adding two click event listeners to my button, one using a function and one using the object. You can see when I click the button that they're both being executed.
[1:26] In practice, I have not seen much usage of using addEventListener with objects that have a handleEvent function property. It seems to be overwhelmingly common practice to use a function as the second argument to addEventListener.
[1:44] You can technically modify an event listener object's handleEvent property after the event listener has been bound, however, I cannot think of any reason why you would want to do this.
[1:58] If you provide a function to addEventListener, then in just the same way as object property event handlers, your function is provided with an event as its only argument. This runtime context of the function is set to the element that your event listener is bound to, which in our case is the button element, though this binding is default binding.
[2:22] Here I am locking the, this runtime context, of my onClick function to be the my this object that I have created. Now the this context of my onClick function is not the default binding, which would have been button, but now my explicitly bound, my this object.
[2:43] Things are different when using addEventListener when you provide an EventListener object. Unlike our function, that this runtime context in our handle event function is not set to the element that the EventListener is bound to, rather it is set to the EventListener object itself.
[3:06] In this case, we can see over here that this is actually being set to our listener object right here. In order to get the element that the EventListener is bound to, you can use the event.current target property, which we can see is pointing to our button.
[3:24] The this keyword operating differently between EventListener objects and functions could potentially be a bit of a gotcha if your EventListener object was borrowing a function that you are also using as an EventListener.
[3:39] In this case, our listener object is using this onclick function that we've defined up here. Here we're adding two EventListeners. One which is the onclick function and one which is our listener object. Now when our button is clicked, our onclick function has a different this value depending on whether it was bound as a function or as a listener object.
[4:04] I personally have not come across this problem as I have not seen that much usage of the EventListener object, but the differing behaviors of the this keyword inside of the executed listener function is something to keep in mind.
[4:20] The third argument to addEventListener can either be an addEventListener options object or a Boolean value. An addEventListener options object has three optional properties.
[4:35] The capture property allows you to specify whether an EventListener will be called in the capture phase or in the bubble phase of an event. If the capture value is true, then the EventListener will be called in the capture phase.
[4:51] If the value is false, then the EventListener will be called in the bubble phase. The default value for capture is false as EventListeners added with addEventListener are executed in the bubble phase by default.
[5:06] Another property is once, and when it is set to true, the EventListener will automatically be unbound after it has been called a single time. Here I have an onclick function that will log out how many times the function has been called. I'm setting once to true, so our onClick function should only ever be called one time.
[5:29] When I come over here and click my button, you can see that no matter how many times I click the button, I'm only logging out the first call. If I set once to false, then my onClick function will continue to count up as its executed.
[5:50] The default value for once is false. By default, an event listener will continue to be called when there is a matching event type. Another property of the addEventListener options object is passive. Generally speaking, the passive value defaults to false, but in some cases, it defaults to true.
[6:11] We will cover passive events in detail in my passive events lesson. For now, all you need to know is that you can set a passive property on your addEventListener options object to mark the event listener as passive. You can use any combination of the addEventListener options properties, and all of them are optional. They will use their default values when no value is provided.
[6:39] The addEventListener API also supports parsing in a single Boolean value as a third argument, mostly for historical support. When a single Boolean is used, that Boolean controls whether the listener will be bound in the capture phase or in the bubble phase of an event. A true value means that the listener will be called in the capture phase, and a false value means that the listener will be called in the bubble phase.
[7:10] The single Boolean syntax is equivalent to using a capture property on an addEventListener options object. In this case, a true value is equivalent to this object with a capture property set to true, and a false Boolean value is equivalent to providing an object with a capture value set to false. For clarity, I always opt for the addEventListener options object syntax over the Boolean syntax for controlling the capture flag.
[7:43] When I'm looking at this code, it can be hard to know what true means. Even if I remember that this controls the capture value, I have to think is true capture, or is true bubble, can't remember, might have to google it. By using the object syntax, my intention is extremely clear. I'm trying to add an event listener in the capture phase. This example is also using the parent element, which sits above the button in the DOM.
[8:09] When creating an event listener, you parse in an event type, a listener reference, and a capture value. These four things, event target, event name, listener reference, and capture value uniquely identify an event listener. You cannot add two event listeners with the same event target, event name, listener reference, and capture value.
[8:28] Here, when I click my button, it will say clicked was only logged once. If I change any of the four of these uniquely identifying properties of an event listener, I will create a new event listener. For example, on the second call, I can change the capture value to be false.
[8:43] Now these two calls to addEventListener don't share the same capture value, and so when I click the button, I will see that clicked is now being logged out twice, showing that I have, in fact, created two different event listeners. I'm now calling button.addEventListener twice, parsing in a different function as the second argument.
[9:01] This is also creating two event listeners, because these two functions do not have referential equality. They are different functions. When I click my button, I can say that my first event listener logged clicked to the console, and my second event listener added clicked2 to the console.
[9:18] Now my second addEventListener call is being bound to the parent event target, and the three arguments to addEventListener are being kept the same between these two calls. When I click on the button, I will see that click is again being logged out twice. I've created two event listeners here. One is attached to the button, and one is attached to the parent.
[9:39] Interestingly, it does not matter how you express the capture value. In this first case, I'm providing an object with a capture property value set to false. In this second call to addEventListener, I'm parsing in false as a third argument, which is equivalent to capture false. In this third call to addEventListener, I'm not providing any capture value, and so the capture value will use the default false value.
[10:02] When I click the button, clicked was only logged once. These three calls to addEventListener all share the same capture value. It was just expressed in a different way. Only the first call to addEventListener created an event listener, because the following two calls use the same event target, event name, listener reference, and capture value.
[10:24] Here, I have two different EventListener objects, one called listener, and one called listener two. Listener two is borrowing the function from my first listener. I'm then calling addEventListener twice, firstly providing in listener and then listener two.
[10:39] When I click my button, I see that first is being logged out twice. This is because addEventListener is doing the reference check on the event listener object and not the handle event function.