6.1 Events
# Events
In JavaScript, an event
is something that happens to an object.
Objects have property values, which are values that can be used to describe an object. Property values can be things like size, speed, colour, name, id, etc. String.prototype.length
is an example of what are generally referred to as a property.
Objects have methods, which are functions that belong to the object that make something happen. It could be something that the Object can do or something that the object does to another Object. Methods are also properties but are just called methods. Array.prototype.sort()
is an example of a method. You can usually tell the difference between them in your code by the parentheses that appear after the name of the method. Properties don't have parentheses.
Event
s are things that happen to an object. A webpage loads. An anchor tag is clicked. A form is submitted. load
, click
, and submit
are all examples of events.
Back in the early days of the web events were usually added through the HTML, like this:
<p onclick="someFunction()">click me</p>
Do NOT do this ^^^^
Avoid putting the onclick
attributes in your HTML. It is considered bad practice and usually interpreted as a sign of a developer that doesn't understand how to work with events in JavaScript.
There were ways of adding events through JavaScript but it varied by browser.
In the early 2000s a standardized way of adding and removing events was created that worked in all browsers.
# Adding and Removing Events
All HTML DOM Element Nodes have a built-in addEventListener
method. The standard way to add an event to a DOM element is like this:
//get a reference to the <h1> in the masthead
let h1 = document.querySelector('.masthead h1');
//add a click event listener to the <h1> element
h1.addEventListener('click', doSomething);
// OBJECT.addEventListener( EVENT_TYPE, FUNCTION_NAME )
2
3
4
5
After these two lines of code run, the user can click on the <h1>
element as many times as they want. Each time the function called doSomething
will run.
Note the lack of parentheses after the function name. If you add the parentheses then the function runs immediately, without waiting for the
click
event.
You can add as many event listeners to an element as you want. Just pass in an event-type and the name of a function.
If you want to remove an event listener from an object then use the removeEventListener
method. Pass to it the combination of event-type and name of function that you want to remove.
h1.removeEventListener('click', doSomething);
An alternative way of adding event listeners is to use an anonymous function.
h1.addEventListener('click', function (ev) {
//this is an anonymous function that will run when the user clicks the h1
});
2
3
The downside of doing this is that you cannot reuse the function somewhere else in your code. So, only use an anonymous function when your function is one or two lines of code and you DEFINITELY won't use it again somewhere else.
# The Event Argument
If you look at the example with the anonymous function above, you will see a variable called ev
that is a function argument which holds the actual Event Object that JavaScript was listening for.
Listening for a click event? The function will be passed a click Event Object
. Listening for a load
event? The function will be passed a load Event Object
. Listening for a submit
event? The function will be passed a submit Event Object
.
Every Event Object
will contain a few standard properties and a few properties that will vary depending on the type of event.
The standard properties include target
and type
. The type
will be the name of event that happened - click
, load
, submit
, etc.
# Event Target
The target
is the element that reported the Event to the Event Listener.
//find a ul with the className userList
let ul = document.querySelector('.user-list');
//add a click listener to the ul.
ul.addEventListener('click', highlightLI);
//the function that will add a CSS class to the LI that was clicked inside the UL
function highlightLI(ev) {
//ev is a standard variable name to represent the event object
//ask for the target of the click event
let li = ev.target;
//add the CSS class `highlight` to the clicked li
li.classList.add('highlight');
}
2
3
4
5
6
7
8
9
10
11
12
13
We can also check to see if it was actually an li
that was clicked. Here is a modified version of the highlightLI
function that checks.
function highlightLI(ev) {
let li = ev.target;
//only add the class if the target was an <li>
// the tagName property of every DOM element is in uppercase
if (li.tagName === 'LI') {
li.classList.add('highlight');
}
}
2
3
4
5
6
7
8
We can also locate a specific element based on the target of a click. Let's start with this HTML
<ul class="movie-list">
<li class="movie" data-id="76759">
<span class="title">Star Wars</span><span class="year">1976</span>
</li>
<li class="movie" data-id="1136608">
<span class="title">District 9</span><span class="year">2009</span>
</li>
<li class="movie" data-id="130827">
<span class="title">Run Lola Run</span><span class="year">1998</span>
</li>
</ul>
2
3
4
5
6
7
8
9
10
11
When a user clicks inside the ul
, depending on the padding and margins, they could be clicking on the ul
or an li
or any one of the span
elements. We have class names on all the elements that will help us to identify which element was clicked. We also have a data-id
attribute in the li.movie
elements.
As a best practice we do NOT want to add click listeners to every one of those items. It would be bad for performance. Instead, we will add ONE listener to the ul
. When it's listener function is triggered we will get the ev.target
and then from that find our li
element.
let ul = document.querySelector('.movie-list');
ul.addEventListener('click', pickMovie);
function pickMovie(ev) {
//get a reference to whatever was clicked inside the ul
let target = ev.target;
//use the `closest` method to find the closest element with the class `movie`
let li = target.closest('.movie');
//it works just like querySelector() except it walks up through the parent elements looking
//for the specific CSS selector match
console.log(`You clicked on movie ${li.getAttribute('data-id')}`);
}
2
3
4
5
6
7
8
9
10
11
12
# Self Removing Event Listeners
There is a third argument for the addEventListener
method. It is an optional Object with a few properties. You can use the once
property set to true
to prevent the event listener from running more than once. It will actually remove the event listener automatically the first time it runs the function.
myElement.addEventListener('click', doSomething, { once: true });
# Event Bubbling and Propagation
Events in the DOM will bubble
by default.
Does this mean that they are happy? No. Does this mean that they are filled with CO2? No.
It means that when an event happens to a DOM element, it will travel up the HTML source through it's parent elements until it reaches the
element.Let's use this basic HTML as a reference:
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<header>
<h1>Some Site Title</h1>
</header>
<main>
<h2>Page Title</h2>
<p>Lorem Ipsum</p>
<p>Lala Palooza <span>Click Me!!!!</span></p>
<p>Lorem Ipsum</p>
<p>Lorem Ipsum</p>
</main>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
We want our user to click on the span with the text Click Me!!!!
. Forgetting any JavaScript that might be attached to the page for a moment, Let's look at what happens with a click event.
The user clicks on the <span>
. The click event will then bubble
to the parent element of the span
. This is the <p>
with the text Lala Palooza
. It will then bubble
to the next parent element - the <main>
element. The <h2>
and the first <p>
are not parent elements, they are siblings of the Lala Palooza paragraph.
From main
it will bubble through body
and then to html
.
If you want to STOP the event from bubbling up to it's parent element then, inside the event listener function, we call the stopPropagation()
method on the Event Object that was passed to the function.
function doSomething(ev) {
//ev is the event object passed to this function
ev.stopPropagation();
}
2
3
4
Sometimes, you might have a DOM element that has more than one listener for the same event. Eg: a button that has multiple click listeners.
Now we are not looking at bubbling
of the event. The event is on the same element and just triggering multiple functions. If we want to stop subsequent listeners on the same element, as well as, stopping the bubbling, we can use stopImmediatePropagation
.
let btn = document.getElementById('myButton');
//add 4 listeners to the button so clicking it will run four functions
//the functions will run in the order that they were added.
btn.addEventListener('click', doTheFirstThing);
btn.addEventListener('click', doTheSecondThing);
btn.addEventListener('click', doTheThirdThing);
btn.addEventListener('click', doTheFourthThing);
//In the 2nd function we are going to check for a setting
//if the setting is false then we do NOT want the 3rd or 4th functions to run.
function doSecondThing(ev) {
if (setting === false) {
ev.stopImmediatePropagation();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Prevent Default
One last event object method that you will frequently use is preventDefault()
.
Some Objects have built-in default behaviours. These are things that will happen without any JavaScript even being loaded on the page.
If you click an anchor - the browser will navigate to a new page.
If you click a submit button - the browser will bundle the data in a form and send it to the server.
If you right-click on a page - the browser will show you a context menu with options.
There will absolutely be times when you want to stop the browser doing the default activity. You will want to use JavaScript to decide what happens next.
//find an anchor, form, and submit button
//give them listeners to call the same sample function
let a = document.querySelector('a#linkHome');
a.addEventListener('click', doSomething);
//forms have a submit event
let myForm = document.querySelector('form#myForm');
myForm.addEventListener('submit', doSomething);
let btnSubmit = document.querySelector('button#btnSubmit');
btnSubmit.addEventListener('click', doSomething);
function doSomething(ev) {
console.log(ev.type);
//could be click or submit
//stop the browser from doing it's default activity
ev.preventDefault();
//now we can do whatever we want...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Page, File, and Navigation Events
There are different events that help us keep track of the progress of loading files and web pages, knowing when scripts are ready to run, when the user is starting to navigate, when the browser is switching between tabs, if storage has been updated, if the page orientation or device orientation changes, when the browser goes online or offline, scrolling and resizing.
Most of these events occur on the global window
object. In the list below the objects that the event happens to are shown.
window.addEventListener('load', (ev) => {
//the window has loaded all html, css, js, fonts, images, etc
//fires after DOMContentLoaded
});
img.addEventListener('load', (ev) => {
//load also fires for images, scripts, audio, video, css files.
});
window.addEventListener('DOMContentLoaded', (ev) => {
//The HTML has been loaded and parse. The DOM is ready to be used.
//first event that happens when loading a page
});
window.addEventListener('pageshow', (ev) => {
//user has navigated to a page and the load event has fired
});
window.addEventListener('beforeunload', (ev) => {
//about to leave the page. last chance to do something with the page
});
window.addEventListener('unload', (ev) => {
//browser is leaving this page. You can't stop it now...
});
window.addEventListener('hashchange', (ev) => {
//the hash value in the URL has changed. navigating within the page.
//happens after popstate
});
window.addEventListener('popstate', (ev) => {
//navigated to a new page or within the history array
//happens after pageshow but before hashchange
});
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
28
- window | img | script
load
- window
DOMContentLoaded
- window
pageshow
/pagehide
- window
beforeunload
- window
unload
- window
hashchange
- window
popstate
- window
online
/offline
: fully looses connection or regains even a weak one - window
orientationchange
: landscape vs portrait - window
deviceorientation
: mobile device rotates along any of the 3 axis - window
error
: when any error happens in your script - window
storage
: web storage updated on another tab - window
beforeprint
/afterprint
- document
visibilitychange
: active tab changes - document
scroll
- document
resize
- document
cut
/copy
/paste
# Mouse Events
When using a mouse there are a number of methods that we can use to track how the user is using the mouse to interact with the webpage. The pointer
and touch
events will become mouse events on non-touch devices.
Remember that click events all bubble.
Every mouse event will give you useful information about the mouse event like x
and y
position.
click
: left clickdblclick
: clicked twice in rapid successionmouseover
/mouseout
: mouse cursor moves over edge of element or leaves content box of elementmouseenter
/mouseleave
: same as over/out except checks parent-child relationshipmousemove
: mouse cursor moves while over the element in questioncontextmenu
: right click on mouse
# Pointer and Touch Events
In addition to the mouse events, there are touch events and pointer events which were added to the HTML5 APIs to handle things like stylus pointers, and multi-touch with fingers.
The built-in touch
events are quite basic. There are no built-in events for things like swipe
, pin
, pinch
, zoom
, or rotate
. If you want those things you will have to use a library or write your own code to calculate changes that happen with touchmove
between a touchstart
and a touchend
event.
- document
pointerup
/pointerdown
- document
pointerenter
/pointerleave
- document
pointerover
/pointerout
- document
pointermove
- document
pointercancel
- document
touchstart
- document
touchend
- document
touchmove
- document
touchcancel
# Keyboard Events
There are three keyboard events. keydown
is the process of pushing a key down. keyup
is releasing the key. keypress
says that both have happened to the same key.
You can attach these events to an <input>
or <textarea>
but it makes more sense to use the appropriate form events for that. The Keyboard events are intended for things like left and right arrows to move through a slideshow or an escape key to close a modal window.
- document | input
keydown
- document | input
keyup
- document | input
keypress
# Form Events
These are the events to use with the form, input, select, and textarea elements.
- input | select | textarea
change
- input | textarea
input
- input | select | textarea
focus
- input | select | textarea
blur
- button
click
- form
submit
# CSS Events
If you have animations or transitions in your CSS that get triggered when you add CSS class names to your elements. We can tap into the animationstart
, animationend
, transitionstart
and transitionend
events to use as triggers to run something else.
NOTE: these events are not 100% guaranteed to fire. If the browser is busy doing many things it is possible that it could miss the end of a transition or animation.
- document
animationstart
- document
animationend
- document
animationiteration
- document
transitionend
# Drag Events
HTML5 has a drag and drop API that lets the user move things around the page. This is a list of the associated events that you can use to build your own drag and drop interactions on your pages.
You will be able to customize the visual item dragged as well as carry data from one place to another.
dragstart
drag
dragend
dragenter
dragleave
dragover
drop
# Custom Events
When you build your own Objects it is also possible for you to make them listen for events.
It is also possible for you to create your own custom events and then attach those events to existing objects or objects that you created.
# What to do this week
TODO
Things to do before next week.
- Read all the content from
Modules 6.1 and 7.1
. - Continue working on the Hybrid Exercises
- Submit Hybrid 6