# Adding and Removing Events
JavaScript is largely event-driven. Now, if you have never programmed with events before, that can seem like a pretty meaningless statement. So, let's break this statement down into smaller parts.
# What is a Browser Event
A browser event is something that happens when a user interacts with a web page or some activity in the browser starts or ends. Examples of events: an image finishes loading; the browser finishes rendering the HTML of the current page; a data file has been brought back from a remote location; the user clicks on an HTML element; the user scrolls a webpage; the user presses a key on the keyboard. All of these things are events.
# What is Event-Driven
When events are the trigger that starts your functions running it means that you are programming in an event-driven manner.
Take the list of event examples from the previous paragraph, and add this phrase in front of each one, "you have a function that runs when".
# Event Listeners
The final part of this process is creating Event Listeners. An event listener is an object that sits in the background, attached to the web page, waiting for a specific type of event to occur. It is the glue between the event and the function that you want to run.
Here is an example that will change the background colour of an h1 element to gold when the user clicks on it.
let h1 = document.querySelector("header h1");
h1.addEventListener("click", highlight);
function highlight(ev) {
ev.target.style.backgroundColor = "goldenrod";
}
2
3
4
5
6
The first line just finds the element to which you want to attach the listener.
The second line creates the event listener object. It adds the event listener object to the h1 element and tells the event listener to listen for a click
event. The second parameter in the addEventListener method is the name of a function to call when the event occurs.
Inside the function declaration there is a variable called ev. This variable will be passed the click event when the function gets called. The event object contains a number of properties that can be quite useful. One of these properties is target. ev.target
is the object with the event listener which called the function.
In our example above ev.target
is the local variable version of the h1 variable. We can also use the keyword this as equivalent to ev.target
.
# Anonymous Functions
Another way of writing the code example from above would be with an anonymous (unnamed) function. Just replace the name of the function with the anonymous function declaration.
let h1 = document.querySelector("header h1");
h1.addEventListener("click", function(ev) {
ev.target.style.backgroundColor = "goldenrod";
});
2
3
4
This code will run the exact same way as the version above.
The difference between the two is that the named function can be reused. It can be called by other parts of your code.
# Leave Off the Parentheses
One very important part of the syntax here is that when we add the name of the function we must omit the parentheses at the end of the function name. If we leave the parentheses at the end of the name then JavaScript will immediately run the function and it is the result of the function that will be attached by the event listener.
var h1 = document.querySelector("header h1");
h1.addEventListener("click", setBold()); //this is BAD
var h2 = document.querySelector("header h2");
h2.addEventListener("click", setBold); //this is GOOD
function setBold(ev) {
ev.target.style.fontWeight = "900";
}
2
3
4
5
6
7
8
9
When the user clicks on the h1 element nothing will happen. In fact, as soon as the page loads we will have a JavaScript error that prevents your code from running. The second line of code will actually run the function setBold as soon as it is read. That means there is no ev object being passed. Since ev is undefined in our function, its first line will fail because it cannot find a property called target inside of undefined. The h2 version would work just fine, if not for the error.
So, remember to omit the parentheses.
# Event Bubbling
When an event is triggered on an element, it will, by default, bubble up from that element through all of its parent elements. Using this HTML as an example.
<body>
<header>
<h1>Main title</h1>
</header>
<main>
<h2>Sub title</h2>
</main>
</body>
2
3
4
5
6
7
8
Let's say that the user clicks on the h2
element. That click will fire on the h2
first, then the main
, and then the body
element. It bubbles up through the HTML from the element to the top of the file passing through each of the parent elements. The click will NOT fire on the header
or h1
elements.
There are ways to stop the event passing to other elements or doing other default things.
ev.preventDefault(); //stop the event from doing any default browser things
ev.stopPropagation(); // do not let the event bubble up
ev.stopImmediatePropagation(); //do not let the event bubble up or trigger any other listeners on the same element for the same event.
2
3
# Target and CurrentTarget
The event
object which is passed to your function has two similar properties - target
and currentTarget
.
//let's pretend that thing1 is the parent element of thing2.
document.querySelector("#thing1").addEventListener("click", wasClicked);
document.querySelector("#thing2").addEventListener("click", wasClicked);
//which means when you click on thing2 both listeners will be triggered
function wasClicked(ev) {
//an example function being called after some element was clicked
console.log(ev.target);
console.log(ev.currentTarget);
}
2
3
4
5
6
7
8
9
10
In our scenario here, the user clicks on #thing2
. This will trigger both listeners. First the one for#thing2
and then the one for #thing1
. However, when the function is running, how do you know why the function is running?
This is why we have target
and currentTarget
. target
will be #thing2
, the thing that was actually clicked by the user... it started the bubbling. currentTarget
is the owner of the event listener. When you look at the code, whatever was written in front of .addEventListener()
will be what is stored inside of currentTarget
.
# Removing Event Listeners
There will be times when you want to remove an event listener. Maybe you are building something that is intended to be clicked only once. So, you want to add the event listener but the first time the function is called you want to remove the listener so the function doesn't get called a second time.
The syntax is almost the exact same as the addEventListener method.
let li = document.querySelector(".mainnav li:first-child");
li.removeEventListener("click", slide);
2
As long as you have a reference to the element that has the listener, you can remove it. You just pass the event type and the name of the function to the removeEventListener
method.
We need to pass both parameters because we can add an unlimited number of listeners to any element. We need to be specific about which one to remove.
let li = document.querySelector(".mainnav li:first-child");
li.addEventListener("mouseover", bounce);
li.addEventListener("click", slide);
li.addEventListener("mousedown", wobble);
li.addEventListener("mouseout", hum);
li.addEventListener("mousemove", spin);
li.removeEventListener("click", slide);
2
3
4
5
6
7
This example shows multiple listeners being added to the same object and then only ONE of them being removed.
# Preventing Link and Form Problems
When you click a link or submit a form the browser wants to take over and actually load a new page in the browser or actually send the form off to the web server. If you want to run your own code and prevent the browser from reloading the current page then we need to add a call to the preventDefault method in our event function.
let a = document.querySelector("mainnav a"); //first link in our main nav
a.addEventListener("click", doSomethingCool);
function doSomethingCool(ev) {
ev.preventDefault();
ev.target.style.textDecoration = "strike-through";
}
2
3
4
5
6
7
This example will prevent the browser from taking the user to the new page by calling the preventDefault method. The preventDefault method is another one of the things built into the Event object passed to the function.
# Forcing Events to Happen
There will be times when you want to trigger one of your event listeners in your code without the user doing anything.
Let's say that you have a function that you are using to display and highlight a message on the screen. In most cases this function is being called when the user clicks a button. You have an event listener attached to the button. That event listener calls the function to display the message and highlight it.
We decide that we are going to dynamically load a new image and the image load event will have a listener to call some other function. We also want to call the function being used by the button click. If we know the name of the function it is easy to call. However, we will have to write code that handles the event object being missing.
So, instead we will tell the browser to pretend that the user clicked on the button. This simulated click will actually trigger the function to display the message and highlight it.
element.dispatchEvent(eventType);
We will delve into this more later.