Events in JavaScript are the backbone of interactivity in the browser. In August 6, 1991, Sir Tim Berners-Lee released the very first website to the world! The World Wide Web was born, but there was no JavaScript, and no one really knew what the internet was anyway. In January 1993, the first commercial web browser, Mosaic, was released. We still didn't have JavaScript, but we had the unstandardized version of the DOM (Document Object Model). In 1995, the very first version of JS was born and was famously written in ten days.
Today we can leverage JavaScript to write web applications, and we can use browser events to make our web applications interactive.
- Understanding events in JavaScript
- Setting up Event Handlers
- Understanding
Event Propagation
and specificallyEvent Bubbling
- Implementing
Event Delegation
for dynamically created elements
Let's imagine web development as a marionette. It's a creepy image, but we're going with it anyway.
Web development has three essential parts.
The HTML is the body of the marionette. CSS comprises the marionette's clothing and haunting, vacant expression. JavaScript is the marionette's strings.
Without the strings, the marionette would be lifeless.
Events are one of the ways we can "pull" the marionette's strings.
Events in JavaScript are a means of "call and response." We can write code to capture users' actions, and made our web applications respond!
Events are constantly happening in the browser, and most of them go unnoticed. To capture
events, we have to add Event Handlers
to our code - Event Handlers
are scripts that are automatically executed when an event occurs.
We're able to reference HTML elements in our JavaScript code to capture specific events - querySelector
and querySelectorAll
are methods on the Document
object (the DOM) that we can use to reference HTML elements.
We can bind Event Handlers
to DOM Nodes with the addEventListener
method. The first argument on the addEventListener
method is the event case, and the second is a callback function.
There are tons of Document
selector methods and Event Handlers
but let's focus on the basics for now.
Further Reading: Event MDN
Events are objects.
Let's take a look at this code. We have a button that's bound to an event listener. On click, we execute an anonymous function. This function is going to log the event object to the console. We're also using the
typeof
operator to log the event's datatype. Take a couple of minutes to look at the properties on theMouseEvent
object. What are some of the potential use cases for these properties?
So, now that we know what events are, and we've talked a little about how events work, let's talk about Event Propagation.
Sometimes the terms Event Propagation
and Event Bubbling
are used interchangeably, but Event Bubbling
is only one phase of Event Propagation.
Let's look at this following HTML code.
<HTML>
...
<body>
<main>
<button>
Click me!
</button>
</main>
</body>
</HTML>
Let's say we click the button in the above example. What happens?
Our click starts Event Propagation
, Event Propagation
encapsulates the entire lifecycle of a DOM Event.
The standard DOM event lifecycle describes three phases of Event Propagation
: capturing, targeting, and bubbling.
- Capturing phase – the event traverses the DOM tree to the target element.
- Target phase – the event has reached the target element.
- Bubbling phase – the event bubbles up from the target element.
An event begins at the Document
object, traverses its way down the DOM Tree
to find its target
- remember that target
property on the event
object? The target
property now comes into play. Once we reach the event.target
, the last phase begins: Event Bubbling.
Event Bubbling
is probably the easiest part of this process to remember. Like bubbles, events float to the top of the DOM tree
. In general, we're going to focus on Event Bubbling.
Note: The capturing phase rarely gets used in code. It usually is invisible to us, but we can expose the capturing phase under special circumstances
Partner up and copy this HTML into a codepen:
<style>
body * {
background-color: rgba(0, 0, 0, .2);
border: 1px solid black;
border-radius: 10px;
min-height: 150px;
margin: 2rem;
padding: 1rem;
}
</style>
<main class="grandparent">
Grandparent
<section class="parent">
Parent
<div class="target">Click me!</div>
</section>
</main>
After you've created a codepen with this boilerplate HTML and styling, add event listeners to the grandparent, parent, and target elements. These event listeners should execute a function that triggers an alert
on a click
event.
How does our event bubble?
- On the
<div>
tag. - Then on the parent
<section>
tag. - Then on the grandparent
<main>
tag. - And so on upwards until we reach the document object.
You'll notice that clicking the Click Me!
<div>
has the same effect as clicking all the elements "above" the <div>
tag.
So if we click on the <div>
tag, then we’ll see 3 alerts: <div>
→ <section>
→ <main>
.
This code is an example of "Event Bubbling" because events "bubble" to the top of the Document
from the inner <div>
element.
A lot of developers struggle to understand when eventListeners are bound to DOM nodes. This process happens when the browser loads our JavaScript code! In our JavaScript code, we can select and bind event listeners to DOM nodes, but! we've only added event listeners to the nodes that are currently on the page when our JS is initialized. We are not adding listeners to nodes that might get added to the page in the future.
Let's look at codepen together. What do you notice about this code?
Capturing and bubbling allow us to implement a powerful event handling pattern, Event Delegation
.
The idea is that if we have a lot of elements handled similarly, then instead of assigning a handler to each of them – we put a single handler on their common parent element.
We're still able to reference the element where the event occurred; the handler gives us event.target
, so we can see where the event happened and handle it.
Let's look at this example This handler is assigned to
<div>
tags, but also runs if you click any nested tag like<p>
or<code>
tags. When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors - usually ending with theDocument.
- Event Delegation: Simplifies initialization and saves memory
- Less code: when adding or removing elements you do not need to add/remove handlers - if you remove an element from the DOM without removing its event listeners you can cause a memory leak
- DOM modifications: we can add and remove HTML elements while retaining interactivity!
Event Delegation
is super useful. It's one of the common patterns for handling DOM events, and it can be simplified into three parts.
- Put a single event handler on the parent event.
- In the event handler – check the event source element using
event.target
. - If the event happened inside an element that interests us, we can handle the event!
Let's look at this example. Can you set up an event handler to utilize
Event Delegation
in this example?
- What is an event?
- What is the difference between
Event Bubbling
andEvent Delegation
- What happens when an event bubbles?
- Why can't we attached
eventListeners
to dynamically created elements? - What does
Event Delegation
reference, why is it important?
A bubbling event bubbles from the target element straight up the DOM tree. Typically, an event bubbles upwards until it reaches the <html>
tag, and then to document object, and some events even reach the window, calling all handlers on the path.
There are several occasions where you may want to stop an event from bubbling.
What are some use cases where you might want to stop Event Bubbling
?
Look at this example. Partner up and do a little research and see if you can find a way to prevent
Event Bubbling
in this codepen.
You might have heard that JavaScript is a single-threaded programming language. What the heck does that mean? The simplest way to explain this concept is that JavaScript can only do one computation at a time.
We can see this concept in action. Open your console (option-command-i) and write a while loop that never ends. As a quick note, do this at your own risk.
while(true){}
Now try interacting with this browser tab. Nothing happens! JavaScript doesn't capture any events because it is preoccupied with the pesky while loop
you wrote in the console. The single thread is stuck and cannot move on to the next event. If you go to Chrome's task manager (under settings → more tools), you should see this tab utilizing nearly 100% of this tab's CPU core resources. Select the tab, click "End Process" and refresh the browser tab to return everything to normal.
Did you know that you can turn off JavaScript in Chrome? Open your dev tools and navigate to the Sources
tab. Press "Command-Shift-P" to run a command. Start typing JavaScript, select Disable JavaScript,
and then press Enter to run the command. JavaScript is now disabled!
Turing off JavaScript is a useful exercise for auditing your performance site's performance and SEO! SEO can be a handy thing to understand if you're a web developer. If the concept is new to you, this video gives a handy quick rundown. If you disable JavaScript, you can see how your web application "looks" to a search engine or web crawler.