Image

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.

  1. Selecting the HTML element button and creating a reference constant named btn
  2. Creating a function called randomBG that returns a calculated number based on the function paramater (num) given
  3. Attaching an event listener so when the button element gets clicked, the randomBG 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.

  1. We declare references to the HTML elements of form,fname,lname, and p.
  2. addEventListener is used on form to add an event listener that expects a submit event type and e for the event object as a parameter
  3. Using an if statement, we check if either fname or lname is empty
  4. If empty, using the event object e's methods, we stop the form from being submitted using e.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

  1. We declare the function random that returns a randomly-generated number.
  2. We declare the function bgChange that returns the value of the generated color
  3. We create a reference to the HTML element "container" and save in memory to a constant named container
  4. 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 from bgChange

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]