What are events in JavaScript
A quick overview of events in JS
By: Ajdin Imsirovic 08 November 2019
In this article, we’ll take a look at events in JavaScript.
Image by codingexercises
Events are things that are constantly happening in our browser, including, but not limited to:
- A user clicking on an element
- A mouse hovering over a button
- A form being submitted
- A web page loads
There are many, many other events: browser window being resized, all the mouse movements on a page, user scrolling down the page, user pressing the keys…
With JavaScript, you have an easy way to get notified when an event happens. This is called listening for events.
You can also do something when an event happens. This is known as handling events.
Using JavaScript, we can run functions when an event happens. This is done with the help of event handlers.
To listen for an event, we use the addEventListener
method.
How do we handle an event? Where do we specify the event handler?
An event handler is placed as the second argument when we call the addEventListener
method. The first argument is the event that we are listening for.
Great, now we have all the necessary ingredients to capture an event, and run an event handler on it!
Here is our snippet’s code:
document.body.addEventListener('click', function() { alert('Click!') });
Run the above code inside the console, then click on the page’s body. For any click anywhere in the body element, you’ll get back an alert.
What happened here?
- We added an event listener on the document’s body element
- We specified that the event we are listening for is the
click
event - We specified the event handler, in the form of an anonymous function. The event handler was
just a simple
alert()
.
How might we improve this simple event handler, and make it more reusable?
We could make our code more modular by putting our anonymous function in a variable, like this:
var eventHandler = function() { alert('Click!') };
document.body.addEventListener('click', eventHandler);
This is useful, because if we wanted to update our event handler, we could do it easier than in the first example.
Let’s look at a more fun example to see this in practice.
let chosenColor = prompt('Type one of these colors: "orange", "yellow", "tomato" ');
console.warn(`
You picked "${chosenColor}".
Now click on the web page to color it in your chosen color!
`);
var eventHandler = function() { document.body.style = `background: ${chosenColor}` }
document.body.addEventListener('click', eventHandler);
Congratulations! You’ve just listened for an event, and handled it when it occured. You now know how to program events in JavaScript!
Inline Event Handlers
The earliest way to deal with JavaScript events was using the onclick
HTML attribute.
This is not the suggested best practice*, but it’s good to know it’s there.
Asterisk:
Actually, it’s a bit more complicated than that. There was a time when this was a good practice, then it wasn’t, and now it’s again used extensively. For example, in Angular we can have this code: <p (click)="someFunction()">
. While obviously, Angular is a TypeScript framework, a lot of the stuff there is basically JavaScript. So inline click events are, I guess, accepted.
Here’s a quick example of an inline click event:
<div onclick="alert('You clicked this div!')">
Just a simple div
</div>
Let’s add a snipept which will dynamically add this onclick
HTML attribute to any web page, so that we can quickly see it in practice, without too much setup.
function addInlineEventHandler() {
let a = document.body;
a.setAttribute("onclick", "alert('You clicked the body')");
}
addInlineEventHandler();
Let’s now look at how JavaScript deals with events.
How JavaScript Deals with Events
JavaScript is always on the lookout for events occuring on any element in the document.
When an event indeed happens, JavaScript will handle that event if we told it to do so - i.e if we attached an event handler to the element in question.
We might say that JavaScript is checking if there are event handlers on any of the elements on the page, and it performs these checks all the time.
Event Propagation
Let’s say we add the following (simplified) structure to our web page:
document.body.innerHTML = `
<div id="one" onclick="alert('Click one')">
<div id="two" onclick="alert('Click two')">
<ul id="three" onclick="alert('Click three')">
<li onclick="alert('Click four')"
id="four"
style="height:100px;
width:100px;
background:aliceblue">
four
</li>
<li onclick="alert('Click five')"
id="five"
style="height:100px;
width:100px;
background:yellowgreen">
five
</li>
</ul>
</div>
</div>
`;
What does the above code do?
It dynamically overrides the contents of the body element, and adds the HTML structure that consists of several levels of tags.
There are two li tags wrapped up by a ul tag, which, in turn, is wrapped up with two div tags.
We also have inline event handlers in the form of onclick attributes on each of these dynamically added elements.
What will happen when we click on the li with the id=”four”?
Interestingly, we will get the following alerts:
- Click four
- Click three
- Click two
- Click one
What is the conclusion?
A click event on a child element will be registered on all the parent elements of that child element.
What we saw above is an example of event bubbling. Event bubbling is simply the way the browser orders events on the document.
Thus, we can conclude that event bubbling is browser behavior in which events are ordered with the inside-out approach.
The event that happened on the innermost tag is dealt with first, then the event that happened on the tag that wraps the innermost tag gets handled, and so on, all the way up to the html tag, which wraps all the other tags.
There are actually two ways to order events in JavaScript. We’ve seen the first one, called event bubbling.
There’s another one, called event capturing.
Event capturing starts with the html element, and then goes into the nested children elements. It digs down into the nested structure until it reaches the most deeply-nested element on which an event occured. Then it goes back the same way it came.
Event bubbling first checks the deeply-nested element, and then bubbles up the document all the way to the html element.
Let’s rewrite the previous code example using the addEventListener
method. This time, we’ll create a whole new HTML file, with a script
tag right above the closing body
tag. This script
tag will hold our addEventListener
methods.
We’ll start with the DOCTYPE, html, and head tags:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
/* to be added */
</style>
</head>
Inside the style
tags, we’ll add the styles to color each nested element on the page differently:
<style>
#one {
background-color: #dedede;
padding: 20px;
border: 1px solid #abcabc
}
#two {
background-color: #cecece;
padding: 30px;
border: 1px solid #789789
}
#three {
background-color: #ababab;
padding: 10px;
border: 1px solid #456456
}
#four {
height: 100px;
width: 100px;
background: aliceblue;
border: 2px solid blue
}
#five {
height: 100px;
width: 100px;
background: yellowgreen;
border: 2px solid green
}
</style>
Now we can add the body
element:
<body>
<div id="one">one
<div id="two">two
<ul id="three">three
<li id="four" style="height:100px;
width:100px;
background:aliceblue">
four
</li>
<li id="five" style="height:100px;
width:100px;
background:yellowgreen">
five
</li>
</ul>
</div>
</div>
<script>
document.getElementById('one').addEventListener('click', function () {
alert('Clicked on one');
});
document.getElementById('two').addEventListener('click', function () {
alert('Clicked on two');
});
document.getElementById('three').addEventListener('click', function () {
alert('Clicked on three');
});
document.getElementById('four').addEventListener('click', function () {
alert('Clicked on four');
});
document.getElementById('five').addEventListener('click', function () {
alert('Clicked on five');
});
</script>
</body>
</html>
Like any other time, we could improve on our code. But this is not the time to refactor.
Instead, I want to show you how to use the third argument that the addEventListener
method can expect.
That argument can either be set to true
or false
.
If it is set to true
, we instruct the browser to order events using event capturing - i.e to order the events from the outermost to the innermost tag.
Alternatively, if it is set to false
, we instruct the browser to order events from the innermost to the outermost tag.
Thus, let’s update our code with true set on all addEventListener methods. The updated script
tag will now look as follows:
<script>
document.getElementById('one').addEventListener('click', function () {
alert('Clicked on one');
}, true);
document.getElementById('two').addEventListener('click', function () {
alert('Clicked on two');
}, true);
document.getElementById('three').addEventListener('click', function () {
alert('Clicked on three');
}, true);
document.getElementById('four').addEventListener('click', function () {
alert('Clicked on four');
}, true);
document.getElementById('five').addEventListener('click', function () {
alert('Clicked on five');
}, true);
</script>
If we ran the above code now, event propagation will be flipped. Here are the alerts, as they will appear, based on the third argument in the addEventListened method set to true:
- Clicked on one
- Clicked on two
- Clicked on three
- Clicked on four (or “Clicked on five”)
However, if you look at the code above, there is a lot of repetition! How can we fix that?
Stopping Event Propagation
Rather than having to add five event listeners on all the different places in the document, it would be much better to have one single place where we’ll capture the event.
We can achieve that by stopping event propagation, with the help of the stopPropagation
method.
So, let’s update our previous code by adding the stopPropagation
method on the li
with id="five"
.
We will also turn on the event bubbling order of events; in other words, we’ll revert to the default behavior.
The only changes to the previous code will again be made inside the script
tags, as follows:
<script>
document.getElementById('one').addEventListener('click', function () {
alert('Clicked on one');
}, false);
document.getElementById('two').addEventListener('click', function () {
alert('Clicked on two');
}, false);
document.getElementById('three').addEventListener('click', function () {
alert('Clicked on three');
}, false);
document.getElementById('four').addEventListener('click', function () {
alert('Clicked on four');
}, false);
document.getElementById('five').addEventListener('click', function (event) {
alert('Clicked on five');
event.stopPropagation();
}, false);
</script>
Test your code by running it in a webpage with the updated file name: addEventListeners-stopPropagation.html
.
Now click on the li
with id="four"
. This is the order of alerts:
- Clicked on four
- Clicked on three
- Clicked on two
- Clicked on one
Next, click on the green li
tag. This time, the only thing that was alerted is:
- Clicked on five
In our event handler for the li
with id="five"
, we called the stopPropagation
method on the event object that we passed into the event handler function. Indeed, stop propagating it did - the event
never bubbled up to the outermost element, like it did when we clicked on the li
with id="four"
.
What is this event object that we just used? We haven’t seen it before. We’ll discuss it soon, but before we do, to avoid information overload and make sure not to jump any staircase, let’s write a few more event handlers first.
We’ll begin by adding an event handler to our page dynamically. The event handler we’ll code is going to add a div
whenever we click on the body
tag of our web page.
Add an Event Handler to our Page Dynamically
Similar to the approach we used in the previous chapter, and in order to practice what we’ve learned, let’s create a new snippet, and call it “Dynamic Onclick Event Handler”.
This is the code to add to our new code snippet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function addDivElement() {
var div = document.createElement('div');
div.innerHTML = `
<div class="dynamic" style="width:100px;height:100px;background:#dedede">
I was triggered by a click event!
<br>
That click event happened on the body element!
</div>
`;
document.body.appendChild(div);
}
var body = document.getElementsByTagName('body');
for (i = 0; i < body.length; i++) {
body[i].addEventListener("click", addDivElement);
}
Looking at the code above, it should be easy understanding lines 1-14, based on what we’ve learned so far.
However, there are still a couple of staircases we’re missing in order to get to the level at which we can comforably understand the code above. So let’s pick it apart.
The code on line 15 runs the getElementsByTagName
method on the document
object. When you run this methdo and pass it any element, what you get back is an HTML Collection. For now, it’s enought to know that an HTML Collection is an array-like data structure in JavaScript. It is basically list a list of variables grouped together under a common label. In our case, that common label is body, and each individual memeber of this HTML collection can be accessed by using the following syntax:
- to get the first member, we run body[0]
- to get the second member, we run body[1]
- to get the third member, we run body[2]
A quick little exercise to understand this concept better:
- Go to a popular website such as yahoo.com.
- Open the browser console. It is likely there will be some junk in the console, so let’s run a built-in browser function to clear the console. The function we need to run is: clear().
- Next, type the following code:
var divs = document.getElementsByTagName('div');
divs[0];
divs[1];
divs[2];
divs[3];
When you run the above code, the console will return something similar to the following:
<div id="UH">...</div>
Of course, the id attribute of each of the divs will be slightly different.
Are you wondering how many div elements are there on the page?
I’m glad you asked, because finding the answer to this question is as easy as running a piece of code multiple times.
In JavaScript (and many other computer languages), running a piece of code many times is referred to as looping.
There are many ways to loop over a block of code - i.e run a block of code multiple times. One of those ways is the for
loop.
Running a Block of Code Multiple Times with a For Loop
Still on the Yahoo homepage, clear the console again by running clear()
.
Next, run the following for loop to see how many divs are there on the page:
var divs = document.getElementsByTagName('div');
for (i=0; i< divs.length; i=i+1) {
console.log(i)
}
Yay, you’ve just written your first for loop! Press the ENTER key to see it count all the divs. When I did it on Yahoo homepage, I got back 376. Pretty large number alright! Then again, Yahoo is a large website with a large homepage.
Ok, so, before we get back to understanding our event handler code, let’s build the for loop staircase.
Since our for loop is there to loop over a block of code X amount of times, we are basically setting up the counter that will keep track of how many times the code has been run.
The for loop can have three distinct pieces of code, separated by semi-colons. The first piece of code is the counter’s initial value. That’s the i=0
part.
The next important piece of code is the exit condition. Our exit condition in the above for loop is i<divs.length
. The length attribute on our HTML collection counts the number of members this collection has. It is starting from 1. Contrary to that, the actual HTML collections (and arrays too!) get counted from zero. This allows us to have this nice piece of code that says: keep on looping the code for as long as the value of i
is less than the number of members in the divs
HTML collection. When the value of i
is the exact same number as the number of divs
in the HTML collection, that means that the exit condition has already been satisfied.
The third important piece of code is the variable update, that is, it answers the question of “What do we do with a variable once a single loop has been completed?”. The answer in our example is this: “Once a loop has been completed, increase the value of i
by one, and assign that value to i
. In
other words, we are telling JavaScript to increase i
by 1 on each loop.
Once we have these 3 pieces of information, we simply add our block of code:
{
console.log(i);
}
What the above block of code does is, it just logs out the current value of i
on each loop.
Since I ended up with the number 376 as the last number logged out to the console, I can safely say that my for loop looped over the above block of code for precisely 376 times.
And it did that really, really fast, much faster than I was able to follow it.
Wow, this was a pretty long explanation!
Luckily, this explanation has de-mystified the code of our event handler. Let’s look at it again. This time, it should be a lot easier to understand:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function addDivElement() {
var div = document.createElement('div');
div.innerHTML = `
<div class="dynamic" style="width:100px;height:100px;background:#dedede">
I was triggered by a click event!
<br>
That click event happened on the body element!
</div>
`;
document.body.appendChild(div);
}
var body = document.getElementsByTagName('body');
for (i = 0; i < body.length; i++) {
body[i].addEventListener("click", addDivElement);
}
On line 1, we define a new function, called addDivElement
.
On line 2, we create a div
, and we bind this place in memory with a label called ‘div’.
On line 3, we set the innerHTML value on our div
object to the code on lines 4, 5, and 6.
On line 7, we close the template literal with a closing backtick and a semi-colon.
On line 9, we append our new div
as a child of the body
element.
On line 13, we run the getElementsByTagName
method on our document
object, and we pass it body
as its argument. JavaScript stores the result of me running this method in memory, and I assign that result - an HTML collection - to the variable called body
. At this point, as a side note, it might be interesting to mention that there should always be a single body
element on an HTML page (unless there are iframes
in it too). So feel free to run this command in your console: body[0], body[1], body[2], and see what comes up.
Finally, on line 15, we run a for
loop, using the counter variable of i
. The number of times the block of code on line 16 will be run depends on the length
of the body
HTML collection. Out of curiosity, to see how many times it was looped over, we added the console.log(i)
on line 17 inside the block of code that’s being looped over.
This brings us to the answer of what will happen if we, for example, type out body[2]
to the console.
The answer is simple: for anything but the first member of HTML collection - body[0] - you will get back the value of undefined
. That’s why the snippet will only log out a zero to the console. The zero is the temporary value of the i
variable, which is supposed to change based on how many times the for loop was ran. However, since the for loop only gets run once - because there is only one member in the body HTML collection - only a zero will be logged out to the console.
Let’s shift our focus to the last three lines of our dynamic event handler code snippet.
for (i = 0; i < body.lenght; i++) {
body[i].addEventListener("click", addDivElement);
}
What we need to focus on is the built-in addEventListener
method. This method accepts two parameters. The first one is the event that will trigger the handler function. The second one is the actual event handler function: the function that will be run to handle the event that happens.
What other events are available to us?
Some Commonly Used Events
At this stage, we’ll only list four events in JavaScript. There are many more events, but we will focus on these three for now, because we need to use them immediately and have some small wins, to keep up our dopamine levels and help us plow through the book easier.
The events we’ll be covering right now are:
- mouseover,
- mouseout,
- keydown, and
- oncontextment
Let’s practice using each one of them, by saving a number of snippets. Here are the new snippet names:
- Practicing onmouseover event
- Practicing onmouseout event
Practicing onmouseover Event
This first snippet will log out a message whenever we hover over an element with a mouse.
Here is the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function addDivElement() {
var div = document.createElement('div');
div.innerHTML = `
<div
id="theDiv"
class="dynamic"
style="width:100px;height:100px;background:#dedede">
Just another div
</div>
`;
document.body.appendChild(div);
}
function changeDivColor() {
var div = document.getElementById('theDiv');
div.style = "width:100px;height:100px;background:yellow";
}
addDivElement();
var body = document.getElementsByTagName('body');
for (i = 0; i < body.length; i++) {
body[i].addEventListener("mouseover", changeDivColor);
}
var reloadFn = function () {
window.location.reload()
}
setTimeout(reloadFn, 5000);
Let’s run the above snippet. The script will first run the addDivElement
function on line 19. Then it will assign the HTML collection into the body
variable, and it will run the for
loop on line 21.
Inside the for
loop, on line 22, the event listener is waiting for the mouseover
event to trigger the changeDivColor
function.
Once you hover over the body element, the changeDivColor
will be ran. We can see from its function definition that it sets the style
property on the div
object. The style
property gets set to the exact same dimensions as before, except the color is changed from #dedede
to yellow
.
Finally, on the last three lines of code, we have some new concepts to explain. These new concepts are window.location.reload()
and setTimeout
.
The role of the window.location.reload()
method is simple: when it’s run, it will refresh the browser. Why are we refreshing the browser? We are refreshing the browser so as to remove the changes we made to the document
object by attaching our mouseover
event handler - and of course,
to learn how to refresh the browser using JavaScript.
The setTimeout
function is also interesting. It’s an example of what’s sometimes called a facade function. A facade function is a function that looks like it’s part of JavaScript, but actually it’s not.
It’s a browser feature, i.e it’s a feature that exists outside of JavaScript, but that we can access via JavaScript nonetheless.
At this point, we won’t go into the details of what facade functions are. Suffice it to say that setTimeout
, in our example, takes an anonymous function (a function without a name), and a number in milliseconds.
If you are at all confused by the term milliseconds, simply remember this: in one second, there are one thousand milliseconds.
Thus, in the last two lines of code in the above code snippet, we first add a label to a place in memory that stores the anonymous function which refreshes the browser, and we call this label the reloadFn
variable.
Then, we run the setTimeout()
facade function, and we pass it two parameters: the reloadFn
function, and the number 5000.
Effectively, translated to plain English, we are telling the browser to set a timer and count for 5 seconds. Once these five seconds are up, run the reloadFn
function (which will refresh the browser, as we already explained).
Practicing a mouseover and a mouseout Event
Let’s add another snipept to our Chrome browser. This time we’ll capture and handle both a mouseover and a mouseout event.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function addDivElement() {
var div = document.createElement('div');
div.innerHTML = `
<div
id="theDiv"
class="dynamic"
style="width:100px;height:100px;background:#dedede">
Just another div
</div>
`;
document.body.appendChild(div);
}
function changeDivColorToYellow() {
var div = document.getElementById('theDiv');
div.style = "width:100px;height:100px;background:yellow";
}
function changeDivColorToGray() {
var div = document.getElementById('theDiv');
div.style = "width:100px;height:100px;background:#dedede";
}
addDivElement();
var body = document.getElementsByTagName('body');
for (i = 0; i < body.length; i++) {
body[i].addEventListener("mouseover", changeDivColorToYellow);
body[i].addEventListener("mouseout", changeDivColorToGray);
}
If you run the above code as a snippet, you’ll notice that the moment you move your mouse over from the DevTools onto the web page, the background color of the div will change from gray to yellow, and vice-versa. We got ourselves a hover effect!
Although it is easy to do a hover effect in CSS, now you also know how to do it in plain vanilla JavaScript, using events. It’s a great feeling knowing how you can actually achieve this effect without CSS, in vanilla JavaScript!
Finally, we are ready to discuss the Event Object.
The Event Object
When we capture an event, this event gets returned as an object. The contents of the event object varies based on the kind of event that was captured.
Let’s now see what the event object looks like, by running a new snippet, which we’ll call The Event Object Practice.
1
2
3
document.body.addEventListener('mouseover', function(event) {
console.log(event);
});
Pay attention to the JavaScript console when you run this event. Whenever you move the mouse over the document’s body, you will get new information logged to the console, such as:
MouseEvent {isTrusted: true, screenX: 475, screenY: 660, clientX: 475...}
If you click the little triangle on the left of the MouseEvent
, you’ll get the detailed listing of this event object. There are a number of different properties and methods, listed in alphabetical order:
altKey, bubbles, button, buttons, cancelBubble, cancelable, clientX, clientY, composed,
ctrlKey...
This is the contents of an actual event object that we are listening for. The data stored inside the object is very useful. For example, if you locate the value of the defaultPrevented
property, you’ll see that it’s set to false.
Try running a slightly different version of the code above:
1
2
3
4
document.body.addEventListener('mouseover', function (e) {
e.preventDefault();
console.log(e);
});
There are two things to observe in the updated code.
First, JavaScript doesn’t care what you call the captured event object when you pass it as the argument to the anonymous event handler function. You can call it event
, whatever
, or, what lots of people do, simply call it e
.
Second, if you locate the defaultPrevented
property on the event object, you’d find that it is now set to true.
You can check this programmatically, rather than digging through the returned event object in the console:
1
2
3
4
document.body.addEventListener('mouseover', function (e) {
e.preventDefault();
console.log(e.defaultPrevented);
});
Now, instead of the entire object, you’ll keep getting back true
, whenever the event gets triggered, or like they say, when it gets “fired”.
Let’s try a more practical example, using a submit button on a regular HTML page.
We’ll begin our example with the DOCTYPE
, and the html
and head
tags:
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
</style>
</head>
Next, we’ll add the body:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<h1>Prevent a form from being submitted</h1>
<form id="formToValidate">
<input
type="text"
id="inputField"
placeholder="Please type 'abc' (without quotes)"
size="35" />
<button type="submit">Submit</button>
</form>
<script>
</script>
</body>
</html>
Now we can add the actual JavaScript code inside the script
tags.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
var theForm = document.querySelector("#formToValidate")
theForm.addEventListener("submit", function (e) {
if (validateForm() != "abc") {
e.preventDefault(); //stop the default 'submit' behavior
alert("Please input 'abc' only!");
} else {
alert("The form is valid and was submitted");
}
});
function validateForm() {
isValid = document.querySelector("#inputField").value;
if (isValid == "abc") {
return "abc";
} else {
return false;
}
}
</script>
Save the above code in a new file and call it preventSubmit.html
.
Now run it in the browser.
If you type anything other than lowercase abc
into the text field and press the Submit button, you’ll get this alert: Please input ‘abc’ only!
However, if you type abc
and press Submit, you’ll be alerted with the following message: The form is valid and was submitted.