# Event listeners

  • Events are triggered all the time inside a webpage
    • The browser itself already generates a number of events, for example after loading the page, when resizing the window, ...
    • The user can also trigger events, for example by clicking on a link, pressing a key, scrolling with the mouse, submitting a form, ...
    • A complete list of all possible events (opens new window) can be found on the Mozilla website
  • With event listeners we can interact (run a function) every time the event takes place
  • On this page we discuss a few commonly used events
    • If you understand how this works you can make your own event listeners to interact with every possible event
  • An event listener has always two (sometimes three) parameters:
    • first the <event> to watch for,
    • second the callback function that will be executed when the event fires
    • third and optional a boolean value to disable event bubbling (default value for bubbling is true)

 





 


<element>.addEventListener('<event>', function(e) {
  // do something when the <event> is triggered
  // 'e' is the event itself
});

// or with an arrow function
<element>.addEventListener('<event>', e => {
  // ...
});
Copied!
1
2
3
4
5
6
7
8
9

# Mouse events

  • In this example, we use four mouse events
event description
click the mouse button is pressed and released on an element
dblclick the mouse button is clicked twice on an element
mouseenter the cursor comes over an element
mouseleave the cursor leaves an element

# Static animation

  • Open es6/events/mouse_1.html and es6/events/mouse_1.js
  • Line 5 to 7: the embedded styles are only used to give both sections a background color
  • Line 14 to 16: every button triggers a click event to show/hide #section1
  • Line 20 to 22: hovering #section2 will rotate #section1 around the X-axis




 
 
 






 
 
 



 
 
 






<html lang="en">
    <head>
        ...
        <style>
            section, div {  margin: 0.5rem; }
            #section1 { background-color: lightsalmon; }
            #section2 { background-color: lightgreen; }
        </style>
    </head>
    <body>
        ...
        <main class="container">
            ...
            <button id="hide" type="button" class="primary">Click to hide #section1</button>
            <button id="show" type="button" class="primary">Click to show #section1</button>
            <button id="toggle" type="button" class="primary">Double-click to toggle #section1</button>
            <section id="section1" class="bordered">
                ...
            </section>
            <section id="section2" class="bordered">
                ...
            </section>
            ...
        </main>
        ...
    </body>
</html>
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# Dynamic animation

  • The "animations" in the previous example are very straightforward
  • Open es6/events/mouse_2.html and es6/events/mouse_2.js
    • In this example, we change the layout with classes instead of inline CSS
      • show/hide will be replaced with a nice slide-up/slide-down effect
      • the rotate effect will be replaced with a 3D effect
      • All effects will take 1 second
  • Line 8: the transition for all effects on #section1 is set to 1 second
  • Line 9: it's not possible to animate the height attribute over time, but you can use the max-height
    (for now, set the max-height to a value higher the actual height of the element)
  • line 10: for the slide-up effect we set overflow to hidden because otherwise only box itself will close, but the content inside the box stays visible
  • Line 13 to 16: adding the class .hide to #section1 provides the slide-up effect:
    • the max-height goes from 300px to 0px
    • the opacity goes from 1 to 0
    • for the slide-down effect we have to remove the class .hide from #section1
  • Line 17 to 19: adding the class .flip to #section1 provides the 3D flip effect
    (without the perspective() it's only a 2D effect)







 
 
 
 

 
 
 
 
 
 
 








<html lang="en">
    <head>
        ...
        <style>
            section, div {  margin: 0.5rem; }
            #section1 {
                background-color: lightsalmon;
                transition: 1s;
                max-height: 300px;
                overflow: hidden;
                opacity: 1;
            }
            #section1.hide {
                max-height: 0;
                opacity: 0;
            }
            #section1.flip {
                transform: perspective(300px) rotateX(180deg);
            }
            #section2 { background-color: lightgreen; }
        </style>
    </head>
    <body>
        ...
    </body>
</html>
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# Window events

  • Problems with the previous example:
    • we need the max-height for the animation, buth we have to "guess" what the value is
    • the actual height of #section1 changes when we resize the page
  • In this example we constantly (re)calculate the real height of #section1 and set it as the max-height of the container
  • This can be done with the resize event listener on the window object
  • Open es6/events/window.html and es6/events/window.js
  • Line 9: remove the max-height of #section1 because we're going to calculate the height dynamically and add/update it as an inline style on #section1
  • Line 14: add !important to the max-height of #section1 because otherwise we can't overwrite the inline max-height








 




 











<html lang="en">
    <head>
        ...
        <style>
            section, div {  margin: 0.5rem; }
            #section1 {
                background-color: lightsalmon;
                transition: 1s;
                /* max-height: 300px; */
                overflow: hidden;
                opacity: 1;
            }
            #section1.hide {
                max-height: 0 !important; /* override inline css with !important */
                opacity: 0;
            }
            #section1.flip { transform: perspective(300px) rotateX(180deg);}
            #section2 { background-color: lightgreen; }
        </style>
    </head>
    <body>
        ...
    </body>
</html>
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# Event bubbling

  • When an event happens on an element, it first runs the callback function of the corresponding event listener after which the event bubbles up the DOM tree
  • Open es6/events/bubbling.html and es6/events/bubbling.js
    • Look at the DOM structure
    • We set a click event on the figure, the img, the figcaption and the b tag inside the figcaption

Event bubbling

  • When you click on the b element, the event bubbles up the DOM tree, and (it looks as) you also have clicked on the figcaption and on the figure!
    • Sometimes this may lead to unexpected behavior ...
<main class="container">
    <h1>Event bubbling</h1>
    <figure>
        <img src="https://picsum.photos/id/724/600/400" alt="Northern light" />
        <figcaption>Photo: <b>Nelly Volkovich</b></figcaption>
    </figure>
    ...
</main>
Copied!
1
2
3
4
5
6
7
8


 




...
bold.addEventListener('click', function (e) {
    e.stopPropagation();
    console.log('bold is clicked');
});

Copied!
1
2
3
4
5
6

# Prevent default action

  • Sometimes you want to prevent the default action from being executed, for example:
    • click on a link but don't go directly to the href
    • do some client-side validation before actually submitting a form
    • ...
  • You can do so by adding the preventDefault() (opens new window) method to the event
  • Open es6/events/preventDefault.html and es6/events/preventDefault.js




 
 

 






...
<main class="container">
    <h1>Dispatch event</h1>
    <h1>Prevent default action</h1>
    <p>Link to <a href="https://bing.com" id="bing">bing.com</a></p>
    <form id="searchForm" action="https://google.com/search" method="get" target="_blank">
        <label for="q">Search on Google</label>
        <input type="text" name="q" id="q" />
        <button type="submit" class="primary">Submit</button>
    </form>
    <pre></pre>
    ...
</main>
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13

# Dispatch (trigger) event

  • The dispatchEvent() (opens new window) method automatically triggers an event
  • Open es6/events/dispatch.html and es6/events/dispatch.js
    • When the page loads, the click event on the button is automatically triggered



 
 



...
<main class="container">
    <h1>Dispatch event</h1>
    <button type="button" class="primary">button</button>
    <pre></pre>
    ...
</main>
Copied!
1
2
3
4
5
6
7

# Event on a NodeList

  • To add an event listener (listening to the same event and with the same callback function) to every element inside a NodeList, you first have to loop over the NodeList and add the event listener inside the loop
  • Open es6/events/nodelist.html and es6/events/nodelist.js
<div id="buttongroup">
    <button class="primary">primary button</button>
    <button class="secondary">secondary button</button>
    <button class="tertiary">tertiary button</button>
</div>
Copied!
1
2
3
4
5

REMARKS: The 'this' keyword

  • In JavaScript the keyword this (opens new window) refers to the object it belongs to
  • In our event, the this keyword refers to the button that was clicked on, so e.target and this mean exactly the same

# Reuse callback functions

  • Sometimes multiple event listeners use the same callback function
  • To keep your code DRY (Don't Repeat Yourself), you can move the callback to a separate function and reuse it for multiple events
    • Functions will be discussed in more detail later in this course
<element>.addEventListener('<event>', function (e) {
    // callback function
});

// is the same as
function cb(e) {
    // callback function
}

<element>.addEventListener('<event>', cb);
Copied!
1
2
3
4
5
6
7
8
9
10

WARNING

  • Never use parentheses when referring to the callback function
    • Good: <element>.addEventListener('<event>', cb);
    • Wrong: <element>.addEventListener('<event>', cb());

# Exercises

  • Open exercises/index.html and make the exercises under the tab ES6 > Event listeners
Last Updated: 5/7/2021, 9:15:24 AM