Events
I. Event Listeners - Handlers
Resource
Events are actions occurring on your webpage, such as mouse-clicks or key-presses. Using JavaScript, we can make our webpage listen and react to these events.
There are three primary ways to go about this:
- Specify function attributes directly on your HTML elements.
- Set properties in the format of
on<eventType>
, such asonclick
oronmousedown
, on the DOM nodes in your JavaScript. - Attach event listeners to the DOM nodes in your JavaScript. → preferred method.
1. Inline Event Listener
In this example:
onclick
is an HTML attribute, an event listener that directly specifies a string containing the JavaScript code to execute when the button clicks. This code is typically a function call but can be any valid JavaScript expression.alert('Hello World')
is the event handler JavaScript code that defines the action to be taken when the button is clicked.
<!-- the HTML file -->
<button onclick="alert('Hello World')">Click Me</button>
→ This solution isn’t ideal because we’re cluttering our HTML elements with JavaScript.
Also, we can only set one onClick
property per DOM element, so we’re unable to run multiple separate functions in response to a click event using this method.
2. Set on<eventType>
on DOM Nodes
In this example:
onclick
is a property in JavaScript similar, but is not exactly the same as theonclick
attribute in HTML.- It allows you to assign a function as an event handler to the click event. When the element is clicked, the function assigned to the
onclick
property is executed.
<!-- the HTML file -->
<button id="btn">Click Me</button>
// index.js
const button = document.querySelector("#btn");
button.onclick = () => alert("Hello World");
→ Moving the JS out of the HTML and into a JS file is better, but there’s still the problem of a DOM element only being able to have one onclick
property.
3. Attach Event Listeners to DOM Nodes
addEventListener
is a JavaScript method used to attach event listeners to DOM elements. It usually takes in two parameters:
eventType
: The event type (e.g.,"click"
,"mouseover"
,"keydown"
)eventHandler
: The function to be executed when the event occurs. This function is often called a "callback function" because JavaScript will "call it back" later when the event happens.
<!-- the HTML file -->
<button id="btn">Click Me Too</button>
// index.js
const button = document.querySelector("#btn");
// First event listener
button.addEventListener("click", () => {
alert("Hello World");
});
// Second event listener
button.addEventListener("click", () => {
console.log("Button clicked!");
});
→ This way allows multiple event listeners if needed, it’s more flexible and powerful, though it’s more complex to set up.
Some useful events include:
- Mouse Events:
click
,mousedown
,mouse up
,dblclick
- Keyboard Events:
keydown
,keyup
,keypress
4. Using 3 Methods with Named Functions
All 3 methods shown above can be used with named functions. Using named functions can clean up the code considerably.
1. Method 1:<!-- the HTML file -->
<!-- METHOD 1 -->
<button onclick="alertFunction()">CLICK ME!</button>
// the JavaScript file
// METHOD 1
function alertFunction() {
alert("YAY! YOU DID IT!");
}
<!-- the HTML file -->
<!-- METHODS 2 & 3 -->
<button id="btn">CLICK ME!</button>
// the JavaScript file
// METHODS 2 & 3
function alertFunction() {
alert("YAY! YOU DID IT!");
}
const btn = document.querySelector("#btn");
// METHOD 2
// assigns the function reference to the event handler
btn.onclick = alertFunction;
// METHOD 3
btn.addEventListener("click", alertFunction);
Q: Why do we use
alertFunction
instead ofalertFunction()
when adding an event listener?
A: When you set up an event listener, you're telling JavaScript: "When this event happens, run this function." You want the function to run when the event occurs, not when you're setting up the listener.
alertFunction
is a reference to the function. It's like pointing to the function and saying "Here's the function I want to run later."alertFunction()
is a function call that immediately calls or invokes the function. This executes the function right away.→ If you used
alertFunction()
, the function would be called immediately when setting up the event listener, not when the button is clicked. The alert would pop up right away, andaddEventListener
would actually receive the return value ofalertFunction
(which isundefined
) instead of the function itself.
5. Attaching Listeners to a Group of Nodes
To add a listener to each of them, we need to iterate through the whole list, like so:
<div id="container">
<button id="1">Click Me</button>
<button id="2">Click Me</button>
<button id="3">Click Me</button>
</div>
// buttons is a node list. It looks and acts much like an array.
const buttons = document.querySelectorAll("button");
// we use the .forEach method to iterate through each button
buttons.forEach((button) => {
// and for each one we add a 'click' listener
button.addEventListener("click", () => {
alert(button.id);
});
});
II. Event Flow
Resource
<!DOCTYPE html>
<html>
<head>
<title>JS Event Demo</title>
</head>
<body>
<div id="container">
<button id='btn'>Click Me!</button>
</div>
</body>
When you click the button, you’re clicking not only the button but also the button’s container, the div
, and the whole webpage.
→ Even flow explains the order in which events are received on the page from the element where the event occurs and propagated through the DOM tree.
There are two main event models: event bubbling and event capturing.
1. Event Bubbling
In the event bubbling model, an event starts at the most specific element and then flows upward toward the least specific element (the document
or window
).
For example, when you click the button, the click
event occurs in the following order:
<button>
<div>
withid="container"
<body>
<html>
document
The click
event goes up the DOM tree, firing on each node along its way until it reaches the document
object. Modern web browsers bubble the even up to the window
object.
2. Event Capturing
In the event capturing model, an event starts at the least specific element and flows downward toward the most specific element.
For example, when you click the button, the click
event occurs with the following order:
document
<html>
<body>
<div>
withid="container"
<button>
3. DOM Level 2 Event Flow
DOM level 2 events specify that event flow has three phases:
- First, event capturing occurs, which provides the opportunity to intercept the event.
- Then, the actual target receives the event.
- Finally, event bubbling occurs, which allows a final response to the event.
4. Event Object
When the event occurs, the web browser passes an Event
object to the event handler. This Event
object contains information about the event, such as its type (click
in this case), the target element on which the event occurred, and any additional data related to the event.
When you define an event listener using addEventListener
, the browser automatically passes an Event
object as the first argument to the event handler function when the event occurs.
let btn = document.querySelector('#btn');
btn.addEventListener('click', function(event) {
console.log(event.type); // output: 'click'
});
The following table shows the most commonly used properties and methods of the event
object:
Note that the
event
object is only accessible inside the event handler. Once all the event handlers have been executed, theevent
object is automatically destroyed.
Property / Method | Description |
---|---|
bubbles | true if the event bubbles |
cancelable | true if the default behavior of the event can be canceled |
currentTarget | the current element on which the event is firing |
defaultPrevented | return true if the preventDefault() has been called. |
detail | more information about the event |
eventPhase | 1 for capturing phase, 2 for target, 3 for bubbling |
preventDefault() | cancel the default behavior for the event. This method is only effective if the cancelable property is true |
stopPropagation() | cancel any further event capturing or bubbling. This method only can be used if the bubbles property is true. |
target | the target element of the event |
type | the type of event that was fired |
1. preventDefault()
method
To prevent the default behavior of an event, you use the preventDefault()
method.
Example:
When you click a link, the browser navigates you to the URL specified in the href
attribute:
<a href="https://www.javascripttutorial.net/">JS Tutorial</a>
However, you can prevent this behavior by using the preventDefault()
method of the event
object:
let link = document.querySelector('a'); link.addEventListener('click',function(event) {
console.log('clicked');
event.preventDefault();
});
→ Clicking the link will now only execute the code within the event handler (console.log('clicked')
), and the browser won't navigate to the linked URL.
- The
preventDefault()
method does not stop the event from bubbling up the DOM to parent elements → parent elements can still have their event listeners triggered for the same event.- An event can be canceled when its
cancelable
property istrue
.
2. stopPropagation()
method
The stopPropagation()
method immediately stops the flow of an event through the DOM tree. However, it does not stop the browser’s default behavior.
<a href="https://www.javascripttutorial.net/">JS Tutorial</a>
btn.addEventListener('click', function(event) {
console.log('The button was clicked!');
event.stopPropagation();
});
document.body.addEventListener('click',function(event)
console.log('The body was clicked!'); // never gets triggered because no bubbling
});
→ stopPropagation()
prevents the click event from bubbling to the body
, but the browser's default behavior of navigating to the linked URL (defined in the href
attribute) still occurs because preventDefault()
is not used.
III. Page Load Events
Resource
- [JavaScript Page Load Events]
When you open a page, the following events occur in sequence:
DOMContentLoaded
- the browser fully loaded HTML and completed building the DOM tree. However, it hasn’t loaded external resources such as stylesheets and images. In this event, you can start selecting DOM nodes or initialize the interface.load
- the browser fully loaded the HTML and also external resources like images and stylesheets.
When you leave the page, the following events fire in sequence:
beforeunload
- fires before the page and resources are unloaded. You can use this event to show a confirmation dialog if you really want to leave the page.- Adding a
beforeunload
listener withpreventDefault()
triggers the browser to display a confirmation dialog for the user, and their choice (OK or Cancel) determines whether navigation proceeds orpreventDefault()
remains in effect. → this is something the browser automatically does.
- Adding a
→ prevent data loss in case you’re filling out a form and accidentally click a link to navigate to another page.
unload
- fires when the page has completely unloaded. You can use this event to send the analytic data or clean up resources.
Handling JavaScript page load events
To handle the page events, you can call the addEventListener()
method on the document
object.
addEventListener('DOMContentLoaded', (event) => {
console.log('The DOM is fully loaded.');
});
addEventListener('load', (event) => {
console.log('The page is fully loaded.');
});
addEventListener('beforeunload', (event) => {
// show the confirmation dialog
event.preventDefault();
// Google Chrome requires returnValue to be set.
event.returnValue = '';
});
addEventListener('unload', (event) => {
// send analytic data
});
IV. Event Delegation
Resource
<ul id="menu">
<li><a id="home">home</a></li>
<li><a id="dashboard">Dashboard</a></li>
<li><a id="report">report</a></li>
</ul>
To handle the click
event of each menu item, you may add the corresponding click
event handlers:
let home = document.querySelector("#home");
home.addEventListener("click", (event) => {
console.log("Home menu item was clicked");
})
let dashboard = document.querySelector('#dashboard');
dashboard.addEventListener('click',(event) => {
console.log('Dashboard menu item was clicked');
});
let report = document.querySelector('#report');
report.addEventListener('click',(event) => {
console.log('Report menu item was clicked');
});
Having many event handlers on a page will directly impact the performance:
- Each event handler is a function which is also an object that takes up memory. The more objects in the memory, the slower the performance.
- It takes time to assign all the event handlers, which causes a delay in the interactivity of the page.
To solve this issue, we can leverage event bubbling. Instead of having multiple event handlers, you can assign a single event handler to handle all the click
events:
let menu = document.querySelector('#menu');
menu.addEventListener('click', (event) => {
let target = event.target;
switch(target.id) {
case 'home':
console.log('Home menu item was clicked');
break;
case 'dashboard':
console.log('Dashboard menu item was clicked');
break;
case 'report':
console.log('Report menu item was clicked');
break;
}
});
- Instead of handling
click
events of the individual children, we can capture theclick
event at the parent element#menu
. - In the
click
event listener, you can access thetarget
property which references the element that dispatches the event. We get theid
of the element that the event fired usingtarget.id
property. - Once we have the element’s
id
, we can have the code that handles the event accordingly.
Handling the too-many-event-handlers problem is called the event delegation. By doing this, you gain the following benefits:
- Less memory usage, better performance.
- Less time is required to set up event handlers on the page.
- The
document
object is available immediately after the HTML is parsed. So as long as the element is rendered, it can start functioning correctly without delay. You don’t need to wait for theDOMContentLoaded
orload
events.
V. Dispatch Event
Resource JavaScript dispatchEvent
1. Event Constructor
Typically, events are generated by user actions (mouse clicks, key presses). In addition, events can be generated from code. To create a new event, we use Event
constructor:
// syntax
let event = new Event(type, [,options]);
// e
let clickEvent = new Event('click');
The Event
constructor takes two parameters:
type
: a string that specifies the event type. e.g.click
.option:
: an object with two optional propertiesbubbles
: a boolean value determining if the event bubbles or not, defaults tofalse
.cancelable
: a boolean value specifying whether the event is cancelable, default tofalse
.
2. dispatchEvent()
method
After manually creating an event, you can fire (dispatch) it on a target element using the dispatchEvent()
method:
element.dispatchEvent(event);
Example:
In this example, we manually create a click
event and fire it on a button. Even if there’s no user click action, the dispatched clickEvent
will still trigger the event handler execution as if the event were generated by user actions.
<button class="btn">Test</button>
let btn = document.querySelector(".btn");
btn.addEventListener("click", () => {
alert("Mouse Clicked!");
})
let clickEvent = new Event("click");
btn.dispatchEvent(clickEvent);
If the event comes from the user's actions, the event.isTrusted
property is set to true
. In case the event is generated by code, event.isTrusted
is false
.
→ examining event.isTrusted
property can check the authenticity of the event.
Dispatching events is useful for testing simulating user interactions and testing how the code reacts to events without actual user input.
3. Custom Events
Resource JavaScript Custom Events
To create a custom event, use the CustomEvent()
constructor.
// syntax
let event = new CustomEvent(eventType, options);
// example
let event = newCustomEvent("highligh", {
detail: {backgroundColor: 'yellow'}
})
The CustomEvent()
has two parameters:
evenType
: a string representing the name of the event.options
: an object has thedetail
property containing any custom information about the event.
After creating a custom event, you need to attach the event to a DOM element and trigger it by using the dispatchEvent()
method.
// syntax
domElement.dispatchEvent(event);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript Custom Event</title>
</head>
<body>
<div class="note">JS Custom Event</div>
<script>
function highlight(elem) {
const bgColor = 'yellow';
elem.style.backgroundColor = bgColor;
// create the event
let event = new CustomEvent('highlight', {
detail: {
backgroundColor: bgColor
}
});
// dispatch the event
elem.dispatchEvent(event);
}
// Select the div element
let div = document.querySelector('.note');
// Add border style
function addBorder(elem) {
elem.style.border = "solid 1px red";
}
// Listen to the highlight event
div.addEventListener('highlight', function (e) {
addBorder(this);
// examine the background
console.log(e.detail);
});
// highlight div element
highlight(div);
</script>
</body>
</html>