~/Home ~/Notes ~/Categories

Event Handling

26 September, 2020  ·   Javascript
  https://www.udemy.com/course/modern-javascript/

Intro to DOM Events

2 ways not to add events

Lets explore 2 different syntaxes which we should not use.

  1. First type actually involves inline html scripting, which is not recommended. It just clutters the markup.
1<button onmouseover="alert('You hovered over me!')">click me!</button>
2<form>
3  <input
4    type="range"
5    min="50"
6    max="100"
7    onclick="console.log('clicked the input')"
8  />
9</form>
  1. We first select an element in Javascript and then do inline html scripting
 1<button id="clicker">CLicker!</button>;
 2
 3const clicker = document.querySelector("#clicker");
 4
 5clicker.onclick = function () {
 6  console.log("You clicked me");
 7};
 8clicker.ondblclick = function () {
 9  console.log("Double click");
10};
11
12clicker.onclick = function () {
13  alert("hello");
14};
15//now we lost the previous data.
16//Onclick is considered just like any other property

addEventListener

Specify the event type and a callback to run

 1const button = document.querySelector("h1");
 2button.addEventListener("click", () => {
 3  alert("You clicked");
 4});
 5
 6button.addEventListener("click", () => {
 7  console.log("Output in console");
 8}); // we get both things as output for one event. one in console and other as alert
 9button.addEventListener("mouseover", function () {
10  button.innerText = "Dont hover on me";
11}); // we are permanently changing the innerText, the text doesnt go back to its prev state
12// to get the text to its prev state. there is another event called `mouseout` use that.
13button.addEventListener("mouseout", function () {
14  button.innerText = "CLick me!";
15}); // now text changes back to prev state

addEventListener() is great, because it is just one method and it will attach to any type of event listener you want(click, double click, mouseover etc…)

important: Dont use arrow functions as call backs, because sometimes we want to access this inside the function and arrow functions doesnt do well with this


Events on Multiple Elements

This is the important topic of event handling. We know how to add multiple events to a single element. how about multiple elements having a single event? How do we take every button on page and add a click event?

  1. select a group of items u want to add events to
  2. loop over them and add eventlistener to each

Note: this refers to individual object onto which we are listening over when adding multiple events

Exercise:

 1<body>
 2  <h1>Pick a color</h1>
 3  <section class="boxes" id="container"></section>
 4  <script src="app.js"></script>
 5</body>;
 6
 7const colors = [
 8  "red",
 9  "yellow",
10  "orange",
11  "green",
12  "blue",
13  "purple",
14  "indigo",
15  "violet",
16];
17const boxes = document.querySelector("#container");
18const heading = document.querySelector("h1");
19const pickAColor = function () {
20  console.log(this);
21  heading.style.color = this.style.backgroundColor;
22};
23
24for (let color of colors) {
25  const box = document.createElement("div");
26  box.style.backgroundColor = color;
27  box.classList.add("box");
28  boxes.appendChild(box);
29  box.addEventListener("click", pickAColor);
30}

Important : So when the function pickAColor is called, ie., when we click on a box, we are never executing pickAColor ourselves, its being called for us. An Event object is passed to it. Event object is automatically called every time we are not capturing it


Event Object

Contains information about a particular event

1const pickAColor = function (evt) {
2  console.log(evt); //MouseEvent Object is returned
3};
1document.body.addEventListener("keypress", function (e) {
2  console.log(e);
3}); //RT: KeyboardEvent

Key Events

There are atleast 3 types of keyevents.

keydown

A Key has been pressed.

When u hold or press any key it is considered as keydown event.

1<input type="text" id="username" placeholder="username" type="text" />
1const username = document.querySelector("#username");
2//we would want event object, because it contain info about which key is pressed
3username.addEventListener("keydown", function (e) {
4  console.log("KEY DOWN");
5});

keyup

A key has been released.

For all keys, first a keydown event is fired followed by a keyup.

1const username = document.querySelector("#username");
2
3username.addEventListener("keydown", function (e) {
4  console.log("KEY DOWN");
5});
6username.addEventListener("keyup", function (e) {
7  console.log("KEY UP");
8});

keypress

A key that normally produces a character value has been pressed. If the key is not a modifier key, the keypress event is sent

Caution: This event is obsolete and differs from browser to browser, better not to use it much

Example

Lets make a todo list

1<h1>Shopping list</h1>
2<input type="text" name="" id="addItem" placeholder="add items in your list" />
3<ul id="items"></ul>
 1const input = document.querySelector("#addItem");
 2const ulItems = document.querySelector("#items");
 3
 4input.addEventListener("keypress", function (e) {
 5  // doesnt allow spaces in the beginning
 6  if (e.which === 32 && !this.value.length) {
 7    e.preventDefault();
 8  }
 9  if (e.key === "Enter") {
10    if (!this.value) return;
11    const item = document.createElement("li");
12    item.innerText = this.value;
13    console.log(item);
14    this.value = "";
15    ulItems.appendChild(item);
16  }
17});

FormEvents & preventDefault

When we press submit, we get navigated to other page or the page gets refreshed if we dont specify any url in action.Lets say we want to stop the form from getting refreshed when we submit. Capture the submit event and stop it from its default behaviour.

Lets take this html snippet

 1<form id="signup">
 2  <input type="text" placeholder="credit card" id="cc" />
 3  <label>
 4    I agree to T&C
 5    <input type="checkbox" id="terms" />
 6  </label>
 7  <select id="veggies">
 8    <option value="brinjal">Brinjal</option>
 9    <option value="tomato">Tomato</option>
10    <option value="onion">Onion</option>
11  </select>
12  <button type="submit">Submit</button>
13</form>

preventDefault

Default behaviour is prevented

1const form = document.querySelector("#signup");
2form.addEventListener("submit", function (e) {
3  e.preventDefault(); //when this is executed default behaviour is prevented
4});

Now this leaves us free to now extract data from the submit event. If we wanted all data at once and send it to an api using AJAX or using a client side request, we can do that. We have flexibility to do something with the data and we can still capture the submit event. What’s nice about doing this way as opposed to capturing each input as it changes every single time is we dont need to attach a bunch of event listeners for every input, by adding a submit event listener there’s just one event we are waiting for, we tell it not to behave, normally it behaves and then we can extract our data in that function.

 1const creditCardInput = document.querySelector("#cc");
 2const termszcheckBox = document.querySelector("#terms");
 3const veggiesSelect = document.querySelector("#veggies");
 4const form = document.querySelector("#signup");
 5form.addEventListener("submit", function (e) {
 6  console.log("cc", creditCardInput.value); //cc 12343434535
 7  console.log("terms", termszcheckBox.checked); // terms true
 8  // we get the value from value attribute, eg: we get brinjal as output instead of Brinjal
 9  console.log("veggiesSelect", veggiesSelect.value); //veggiesSelect tomato
10  e.preventDefault();
11});

We are accessing data from the form. After accessing these values , we can generally send form data to DB or append something to page using form data. We can put preventDefault() at the top of the function, it still works the same


input and change Events

input

This event is triggered whenever an input changes .We can actually listen to all 3 above inputs(textbox, checkbox and select) at once using a single event type.

Our goal is to create a datastructure which automatically updates whenever a user enters value in input, instead of waitiing for user to submit(like from the above section)

1creditCardInput.addEventListener("input", (e) => {
2  console.log("CC Changed", e); // the event is triggered whenever we type something in the input box
3});

Storing value to an object as soon as user changes the input. These events trigger before user clicks submit.

 1const formData = {};
 2creditCardInput.addEventListener("input", (e) => {
 3  console.log("CC Changed", e);
 4  //formData['cc'] = creditCardInput.value; -> hard coding. instead use event object properties to get value
 5  formData["cc"] = e.target.value;
 6});
 7veggiesSelect.addEventListener("input", (e) => {
 8  console.log("veggie change", e);
 9  formData["veggie"] = e.target.value;
10});
11termszcheckBox.addEventListener("input", (e) => {
12  console.log("terms changed", e);
13  formData["terms"] = e.target.checked;
14});

Refactor the above code. add a name attribute to each html input

 1<input type="text" placeholder="credit card" id="cc" name="creditcard" />
 2<label>
 3  I agree to T&C
 4  <input type="checkbox" id="terms" name="agreeToterms" />
 5</label>
 6<select id="veggies" name="selectedVeggie">
 7  <option value="brinjal">Brinjal</option>
 8  <option value="tomato">Tomato</option>
 9  <option value="onion">Onion</option>
10</select>
 1for (let input of [creditCardInput, termszcheckBox, veggiesSelect]) {
 2  input.addEventListener("input", (e) => {
 3    if (e.target.type === "checkbox")
 4      formData[e.target.name] = e.target.checked;
 5    else formData[e.target.name] = e.target.value;
 6  });
 7}
 8// more sophisticated code
 9for (let input of [creditCardInput, termszcheckBox, veggiesSelect]) {
10  input.addEventListener("input", ({ target }) => {
11    // destructure event object since we only use target
12    // destructure more coz we use only these 4 properties in target
13    const [name, type, value, checked] = target;
14    formData[name] = type === "checkbox" ? checked : value;
15  });
16}

change

If we change the above event type to change it will still behave the same except for the textbox. Textbox input change wont trigger until we lose focus over it or press return key after entering complete data or focus it, unlike input where it triggers event for every single key typed(every single letter changed in text box).

1for (let input of [creditCardInput, termszcheckBox, veggiesSelect]) {
2  input.addEventListener("change", ({ target }) => {
3    // destructure event object since we only use target
4    // destructure more coz we use only these 4 properties in target
5    const [name, type, value, checked] = target;
6    formData[name] = type === "checkbox" ? checked : value;
7  });
8}

This type of pattern (using name attribute) is pretty common especially if we get to something like react and some of the other frameworks or libraries out there. We use name of an input as a key to store the value from that input under, to create a nice object that contains all of our form data at once.

 javascript  dom  es6  events
Manipulating the DOM↠ ↞Machine learning 101