Javascript's Events
Informational
April 23, 2022
- Javascript
- Events
What are Javascript Events?
Most client-side Javascript programs are all event-driven. Compared to other languages where the program waits for user input to perform a set of instructions, the browser generates an event when the user interactions with the keyboard, touchscreen, window, etc. According to MDN, (events are actions or occurrences that happen in the system you are programming, which the system tells you about so your code can react to them.)[https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events] Events control what happens when a user does a physical event for example, clicking on a button to retrieve server data or selecting a checkbox, or even resizing a window. Long story short, events are "things" that happen to HTML elements. Javascript has an 'Event' object that has different methods to allow you to manipulate the Document Object Model (or DOM) to perform in certain ways.
Using Event handlers
Events can be thought of as "signals." When an event gets fired, code has been signaled to react to whatever was triggered in a specific way. But in order to react to an event, you need to register an event handler, as well known as event listeners.
Using the example noted on MDN, let's look at what it means to register an event.
<button className="btn">Click me to change BG color</button>
// select the 'button' element in HTML
const btn = querySelector("button");
// function that returns random color's value
function randomBG(num) {
return Math.floor(Math.random() * (num + 1));
}
// Define and register event listener to element, and alter background color of the document's body when clicked.
btn.addEventListener("click", () => {
const rndCol = `rgb(${randomBG(255)}, ${randomBG(255)}, ${randomBG(255)})`;
document.body.style.backgroundColor = rndCol;
});
Here, we can see a 3 different things going on.
- Selecting the HTML element
button
and creating a reference constant namedbtn
- Creating a function called
randomBG
that returns a calculated number based on the function paramater (num
) given - Attaching an event listener so when the
button
element gets clicked, therandomBG
function runs and set's the background color to the document's body to what gets returned.
EventTarget.addEventListener
takes in at least two different paramaters. The first (in this sense, the string 'clicked'
) represents
the name of the event we want to register the handler for. The second paramater is the function we
want ran in response for what event gets called.
Most people make the function parameter a named function for simplicity's sake, like the example below.
const btn = document.querySelector("button");
function randomBG(num) {
return Math.floor(Math.random() * (num + 1));
}
function changeBackground() {
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
document.body.style.backgroundColor = rndCol;
}
btn.addEventListener("clicked", changeBackground);
Removing Event listeners
You can also remove event listeners. Creating an event listener isn't free, so Javascript gives you a chance to remove them in order to free-up
a bit of memory. To do so, you can call EventTarget.removeEventListener
to remove (or "cleanup") the function.
Let's reuse the code from above.
btn.removeEventListener("click", changeBackground);
EventTarget.removeEventListener
shares the same syntax as EventTarget.addEventListener
, accepting the paramaters of an event type as well as a handler (or reference to a handler).
This also gives you the chance to manipulate the dom in special ways. Lets say an element gets visually mounted (or rendered) in the DOM and you add an event listener to do a specific task. That's easy. But removing an event listener gives you the option to do more tasks when unmounting.
The Event Object
In some cases, you might see the event handler function pass in a paramater e
,evt
, or event
. This passes in the Event object.
The Event Object gives you more features and information about the event target, which in turn can make developing easier.
Using the code from above, I'll explain how it looks and what it does.
const btn = document.querySelector("button");
function randomBG(num) {
return Math.floor(Math.random() * (num + 1));
}
function changeBackground(e) {
// Passing in Event object as parameter
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
e.target.style.backgroundColor = rndCol; // Sets the target of the event's background color
}
btn.addEventListener("clicked", changeBackground);
This snippet differs in above because now the target of the event (which is the HTML element saved in reference btn
) gets the changes
instead of the body of the document. The target property of the event object is always a reference to the element the event occurred on.
Default Behavior
Sometimes when working with events, you might not want the default behavior expected to continue. Using the event target, you can stop something from doing so. One of the most common places to prevent default event behavior is when working with forms. When a form button gets clicked, the default behavior is to submit what the value of the input fields to be sent to a server so data can be transfered. But a good practice is to check the values first, and display some type of alert in case the value isn't acceptable.
For example...
<form>
<div>
<label for="fname">First name: </label>
<input id="fname" type="text" />
</div>
<div>
<label for="lname">Last name: </label>
<input id="lname" type="text" />
</div>
<div>
<input id="submit" type="submit" />
</div>
</form>
Let's say we have a simple HTML form that takes in a users first & last name. It also has a button that submits the input values to the server. But can we check if the values inputted are acceptable?
const form = document.querySelector("form");
const fname = document.getElementById("fname");
const lname = document.getElementById("lname");
const para = document.querySelector("p");
form.addEventListener("submit", (e) => {
if (fname.value === "" || lname.value === "") {
e.preventDefault();
alert("You need to fill in both names!");
}
});
Let's walk through what's happening.
- We declare references to the HTML elements of
form
,fname
,lname
, andp
. addEventListener
is used onform
to add an event listener that expects asubmit
event type ande
for the event object as a parameter- Using an
if
statement, we check if eitherfname
orlname
is empty - If empty, using the event object
e
's methods, we stop the form from being submitted usinge.preventDefault()
and display an alert warning the user
If this form gets submitted without the correct information we're looking for, it will now alert an error statement and not submit to the server.
Event Bubbling & Capturing
It's important to note that the broswer checks if an element that has an event listener is the child of a parent element that also has an event listener. If so, then the browser will fire the parent element's event as well as the child's. Below shows how this works.
<body>
<div id="container">
<button>Click me!</button>
</div>
<pre id="output"></pre>
</body>
const output = document.querySelector("#output");
function handleClick(e) {
output.textContent += `You clicked on a ${e.currentTarget.tagName} element\n`;
}
const container = document.querySelector("#container");
const button = document.querySelector("button");
document.body.addEventListener("click", handleClick);
container.addEventListener("click", handleClick);
button.addEventListener("click", handleClick);
The outcome of clicking the button
element would result in...
You clicked on a BUTTON element
You clicked on a DIV element
You clicked on a BODY element
As you can see, first the child (button
) element's event listener fires, then the parent (container
), then the parent of that (document
). This is known as propagation or 'bubbling up.
Event bubbling and capture are terms that describe phases in how the browser handles events targeted at nested elements. When an event is fired on an element that has parent elements, browsers run three different phases — the capturing phase, the target phase, and the bubbling phase.
The Capturing Phase
The browser checks to see if the element's outer-most ancestor has a click event handler registered on it for the capturing phase, and runs it if so. Then it moves on to the next element inside and does the same thing, then the next one, and so on until it reaches the direct parent of the element that was actually clicked.
The Target Phase
The browser checks to see if the target property has an event handler for the click event registered on it, and runs it if so.
Then, if bubbles parameter is true, it propagates (or "bubbles up") the event to the direct parent of the clicked element, then the next one,
and so on until it reaches the <html>
element. Otherwise, if bubbles paramater is false, it doesn't propagate the event to any ancestors
of the target.
The Bubbling Phase
The browser checks to see if the direct parent of the clicked element has a click event handler registered on it for the bubbling phase,
and runs it if so. Then it moves on to the next immediate ancestor element and does the same thing, then the next one,
and so on until it reaches the <html>
element.
In modern browsers, by default, all event handlers are registered for the bubbling phase. So in our current example,
when you click the button, the event bubbles from the <button>
element outwards to the <html>
element. Along the way:
- It finds the click handler on the
container
element and runs it - Then it finds the click handler for the
body
element and runs it.
The standard Event
object has a function available on it called stopPropagation()
which, when invoked on a handler's event object,
makes it so that the first handler is run but the event doesn't bubble any further up the chain, so no more handlers will be run.
So if we change our event handler function to ...
function handleClick(e) {
e.stopPropagation();
output.textContent += `You clicked on a ${e.currentTarget.tagName} element\n`;
}
... the only text that will be displayed is the one who's target has been clicked.
Event Delegation
Event bubbling can have you scratching your head, but it's also pretty convenient. Let's say we have an example of a user being able to interact with any of a number of child elements to a parent element. Instead of creating event listeners for each child element, you can add an event listener to the parent and have events that happen on the children elements propagate to the parent rather than having to set the event listener on every child individually.
Wait....huh? Sounds a bit complex, but really simple in theory. View this example taken from MDN.
<style>
.tile {
height: 100px;
width: 25%;
float: left;
}
</style>
<body>
<div id="container">
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
<div class="tile"></div>
</div>
</body
Here, we have a parent element named container
with 10 children elements
each sharing the class name tile
.
If we wanted to have an event listener that changes the background color of any specific tile to a randomly produced background value, we can either add an event listener to each child element, or we can add an event listener to the parent. View the javascript code below.
function random(number) {
return Math.floor(Math.random() * number);
}
function bgChange() {
const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
return rndCol;
}
const container = document.querySelector("#container");
container.addEventListener(
"click",
(event) => (event.target.style.backgroundColor = bgChange())
);
Step by step walk through of what's going on is
- We declare the function
random
that returns a randomly-generated number. - We declare the function
bgChange
that returns the value of the generated color - We create a reference to the HTML element "container" and save in memory to a constant named
container
- We add a click event listener to
container
. There, we pass in the event object and set the background color of the event's object target to whatever gets generated frombgChange
And that's all! Events are one of the most important parts when working with Javascript, and they're extremely useful
in order to add more spice to your web app. There's a bunch of different event categories with different
event types (some being Window
& Document
have a load
event type or video elements with the playing
or volumechange
event type).
It's worth taking the time to figure out what you're trying to build, and looking into what event types you might be working with.
(For a list of different Web APIs and links to their event types, you can view this mage on the MDN site)[https://developer.mozilla.org/en-US/docs/Web/Events] (For a much more in-depth look at how events work, consider going to the following link.)[https://html.spec.whatwg.org/multipage/webappapis.html#events]