12: Event Listeners

Invoking functions with event listeners, using external function declarations or internal anonymous or arrow functions.

Learning Goals

At the end of this Lesson you will be able to:

  • Invoke functions with event listeners, using external function declarations.
  • Invoking functions with event listeners, using internal anonymous or arrow functions.

In your javascript/exercises folder, create a new sub-folder named 12.

Save the exercise file below to this new javascript/exercises/12 sub-folder.

Event Listeners: Exercises

About event listeners

Event listeners are the recommended way to invoke functions within modern JavaScript code. The syntax takes two possible forms:

  • External function declaration: An event listener can call a stand-alone function located anywhere in the JavaScript program.
  • Internal anonymous or arrow functions: Alternatively, the code fired by the event listener can positioned inside the event listener itself.

An advantage of event listeners is that they are always located in the JavaScript code, rather than mixed in with the page content and HTML tags.

External, anonymous and arrow functions

You can work with JavaScript events with any one of three types of functions: external functions, anonymous functis, and arrow functions.

In the example below, a function named showBtnClick() that is external to the click event, is used to process the event.

// get button
const button = document.getElementById('button_1');
// attach external function without parenthesis ()
button.addEventListener('click', showBtnClick);

// external function
function showBtnClick() {
    console.log("You clicked button_1")
}

Another approach is to create an anonymous function internal to the click event. See below.

// get button
const button = document.getElementById('button_1');
// attach and create anonymous function with parenthesis ()
button.addEventListener('click', function() {
   console.log("You clicked button_1")
});

And finally, you can shorten the anonymous function by writing it with the arrow function syntax. See below.

// get button
const button = document.getElementById('button_1');
// attach and create anonymous arrow function with parenthesis ()
button.addEventListener('click', () => {
    console.log("You clicked button_1")
});

The JavaScript event object

In JavaScript, an event object is an object that contains information about an event that has occurred, such as a mouse click, keypress, or touch. The event object is automatically passed to an event handler function when the event occurs, and it contains properties and methods that allow you to access and manipulate information about the event.

// get button
const button = document.getElementById('button_1');
// attach and create anonymous arrow function with parenthesis ()
button.addEventListener('click', function (event) {
    console.log("You clicked the button with an ID of "+event.target.id);
    console.log("The clicked button has a class of " +event.target.className);
});

In this example, the addEventListener() method is used to attach a click event listener to a button element with an id of button_1. The event listener function is passed the event object as a parameter, which can then be used to access information about the event.

Here is the samee example using an arrow function.

// get button
const button = document.getElementById('button_1');
// attach and create anonymous arrow function with parenthesis ()
button.addEventListener('click', (event) => {
    console.log("You clicked the button with an ID of "+event.target.id);
    console.log("The clicked button has a class of " +event.target.className);
});

The most common properties of a button or other clicked object you will want to access are the id and className.

Often, the event object is represented simply by the letter e. See below.

const button = document.getElementById('button_1');
button.addEventListener('click', function(e) {
   console.log(e);
});

And here is the most concise way of writing the previous arrow function.

// get button
const button = document.getElementById('button_1');
// attach and create anonymous arrow function with parenthesis ()
button.addEventListener('click', e => {
    console.log("You clicked the button with an ID of "+e.target.id);
    console.log("The clicked button has a class of " +e.target.className);
});

Here is a second example.

document.addEventListener('keypress', function(event) {
    if (event.key === 'r') {
	  document.body.style.backgroundColor = 'red';
    } else if (event.key === 'g') {
	  document.body.style.backgroundColor = 'green';
    } else if (event.key === 'b') {
	  document.body.style.backgroundColor = 'blue';
    }
});

In this example, the addEventListener() method is used to attach a keypress event listener to the document object. In this case, the event.key property is used to determine which key was pressed, and the background color of the page is changed accordingly.

Event capture and bubbling

In JavaScript, event capture and bubbling refer to two different ways that events are propagated ('travel') through the DOM (Document Object Model) from the event target to its parent (above) and ancestor (below)elements.

Event capture

Event capture occurs first and propagates from the top of the DOM tree (the window object) down to the target element. Once the event reaches the target element, it triggers the event listener function that is attached to the target.

Event bubbling

Bubbling, on the other hand, occurs after the event listener function has been triggered and propagates from the target element back up through the DOM tree to the window object. During the bubbling phase, each parent element of the target element also triggers its own event listener function, if one is attached.

Here's an example to illustrate event capture and bubbling:

<body
   <div id="outer">
      <div id="inner"></div>
   </div>
  
	<script>
	  const outer = document.querySelector('#outer');
	  const inner = document.querySelector('#inner');
  
	  outer.addEventListener('click', function() {
		console.log('Outer clicked');
	  }, true); // use capture
  
	  inner.addEventListener('click', function() {
		console.log('Inner clicked');
	  }, false); // use bubbling
	</script>
  </body>

In this example, we have an outer div and an inner div element nested inside it. We've attached a click event listener to each element, using the third parameter of the addEventListener() method to specify whether to use event capture or bubbling.

When you click on the inner div element, the event first travels down the DOM tree from the window object to the target element, triggering the outer event listener function ("Outer clicked") along the way because we've used event capture. Then, the event reaches the inner div element and triggers its own event listener function ("Inner clicked"). Finally, the event travels back up the DOM tree from the target element to the window object, triggering the outer event listener function ("Outer clicked") again, but this time because we've used event bubbling.

Preventing event propagation

To prevent event propagation (either event capture or event bubbling) on a button in JavaScript, you can use the event.stopPropagation() method in the event listener function that is attached to the button.

Here's an example of how to use event.stopPropagation() to prevent event bubbling when a button is clicked:

const button = document.querySelector('button');

button.addEventListener('click', function(event) {
   event.stopPropagation(); // prevent event from bubbling up
   console.log('Button clicked');
});
	
document.addEventListener('click', function() {
  console.log('Document clicked');
});

In this example, we've attached a click event listener to a button element and used event.stopPropagation() to prevent the click event from bubbling up the DOM tree and triggering the click event listener attached to the document object.

Without event.stopPropagation(), when the button is clicked, both the button event listener function ("Button clicked") and the document event listener function ("Document clicked") would be triggered. But now only the button event listener function is triggered and the document event listener function is prevented from being triggered.

Event delegation

Event delegation is a technique in JavaScript that allows you to attach a single event listener to a parent element, which can handle events that are triggered by its child elements. This can be useful when you have many child elements that need event listeners attached to them, as it can reduce the amount of event listeners you need to write and manage.

Here's an example of using event delegation to handle button clicks based on the button ids:

const container = document.querySelector('#container');

container.addEventListener('click', function(event) {
   if (event.target.id === 'btn_1') {
      console.log('Button btn_1 clicked');
   }
   else if (event.target.id === 'btn_2') {
	console.log('Button btn_2 clicked');
   }
});

Preventing default events

In JavaScript, the event.preventDefault() method is used to prevent the default behavior of an event from occurring. This method can be called inside an event listener function to prevent the default action of an event, such as submitting a form or following a link.

Here's an example of using event.preventDefault() to prevent the default behavior of a link:

const link = document.querySelector('a');

link.addEventListener('click', function(event) {
    event.preventDefault(); // prevent link from being followed
    console.log('Link was clicked');
});

In this example, we've attached a click event listener to a link element using addEventListener. When the link is clicked, the event listener function is triggered, and we use event.preventDefault() to prevent the link from being followed and the page from navigating away.

Instead, we log a message to the console indicating that the link was clicked. This way, we can handle the click event in a custom way without affecting the default behavior of the link.

The event.preventDefault() method is typically used to prevent the default behavior of form submissions, which is to submit the form to a server and reload the web page.

Here's an example of using thos method to prevent a form from submitting:

const form = document.querySelector('form');

form.addEventListener('submit', function(event) {
    event.preventDefault(); // prevent form from submitting
    const formData = new FormData(event.target);
    console.log(formData.get('username'));
});

In this example, we've attached a submit event listener to a form element using addEventListener. When the form is submitted, the event listener function is triggered, and we use event.preventDefault() to prevent the form from submitting and reloading the page.

Instead, we create a FormData object using the event.target (i.e., the form element that was submitted), and log the value of the "username" field to the console using the formData.get() method.

By preventing the default behavior of the form, we can handle the form data ourselves using JavaScript and create a more seamless and dynamic user experience. This can be particularly useful when working with single-page applications or other types of web applications that require dynamic form handling.