JavaScript in the browser uses an event-driven programming model.
Usually things start by responding to an event.
The event could be the DOM is loaded, or an asynchronous request that finishes fetching, or a user clicking an element or scrolling the page, or the user types on the keyboard.
In this lesson we’ll analyze user activated events coming from the mouse or touch devices, like mouse clicks or tap events on mobile devices.
Event handlers
You can respond to any event using an Event Handler, which is a function that’s called when an event occurs.
You can register multiple handlers for the same event, and they will all be called when that event happens.
JavaScript offer three ways to register an event handler:
Inline event handlers
This style of event handlers is very rarely used today, due to its constraints, but it was the only way in the JavaScript early days:
<a href="#" onclick="alert('link clicked')">A link</a>
DOM on-event handlers
This is common when an object has at most one event handler, as there is no way to add multiple handlers in this case:
document.querySelector('a').onclick = () => {
alert('link clicked')
}
You can check if an handler is already assigned to an element using
if ('onclick' in document.querySelector('a')) {
alert('onclick handler already registered')
}
Using addEventListener()
This is the modern way. This method allows to register as many handlers as we need, and it’s the one you will find mostly used in the wild:
window.addEventListener('load', () => {
//window loaded
})
Listening on different elements
Sometimes addEventListener
is called on window
, sometimes on a specific DOM element. Why?
It’s a matter of determining how large of a net you want when you are looking at intercepting events.
You can listen on window
to intercept “global” events, like the usage of the keyboard, and you can listen on specific elements to check events happening specifically on them, like a mouse click on a button.
The Event object passed to the event handler
An event handler gets an Event
object as the first parameter:
const link = document.getElementById('my-link')
link.addEventListener('click', event => {
// link clicked
})
This object contains a lot of useful properties and methods, like:
target
, the DOM element that originated the eventtype
, the type of eventstopPropagation()
, called to stop propagating the event in the DOM
and more. You can see the full list here.
Each specific kind of events, like a mouse click, a touch event, a keyboard event, all implement an event that extend this base Event object. We are talking about mouse and touch events in this lesson, so we are interested in these 2:
Mouse events
When looking at mouse events we have the ability to interact with
mousedown
the mouse button was pressedmouseup
the mouse button was releasedclick
a click eventdblclick
a double click eventmousemove
when the mouse is moved over the elementmouseover
when the mouse is moved over an element or one of its child elementsmouseenter
when the mouse is moved over an element. Similar tomouseover
but does not bubble (more on this soon!)mouseout
when the mouse is moved out of an element, and when the mouse enters a child elementsmouseleave
when the mouse is moved out of an element. Similar tomouseout
but does not bubble (more on this soon!)contextmenu
when the context menu is opened, e.g. on a right mouse button click
Events overlap. When you track a click
event, it’s like tracking a mousedown
followed by a mouseup
event. In the case of dblclick
, click
is also fired two times.
mousedown
, mousemove
and mouseup
can be used in combination to track drag-and-drop events.
Be careful with mousemove
, as it fires many times during the mouse movement. We need to apply throttling, which is something we’ll talk more when we’ll analyze scrolling.
When inside an eventh handler we have access to lots of properties.
For example on a mouse event we can check which mouse button was pressed by checking the button
property of the event object:
const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
// mouse button pressed
console.log(event.button) //0=left, 2=right
})
Here are all the properties we can use:
altKey
true if alt key was pressed when the event was firedbutton
if any, the number of the button that was pressed when the mouse event was fired (usually 0 = main button, 1 = middle button, 2 = right button). Works on events caused by clicking the button (e.g. clicks)buttons
if any, a number indicating the button(s) pressed on any mouse event.clientX
/clientY
the x and y coordinates of the mouse pointer relative to the browser window, regardless of scrollingctrlKey
true if ctrl key was pressed when the event was firedmetaKey
true if meta key was pressed when the event was firedmovementX
/movementY
the x and y coordinates of the mouse pointer relative to the position of the last mousemove event. Used to track the mouse velocity while moving it aroundregion
used in the Canvas APIrelatedTarget
the secondary target for the event, for example when movingscreenX
/screenY
the x and y coordinates of the mouse pointer in the screen coordinatesshiftKey
true if shift key was pressed when the event was fired
Touch events
Touch events are those events that are triggered when viewing the page on a mobile device, like a smartphone or a tablet.
They allow you to track multitouch events.
We have 4 touch events:
touchstart
a touch event has started (the surface is touched)touchend
a touch event has ended (the surface is no longer touched)touchmove
the finger (or whatever is touching the device) moves over the surfacetouchcancel
the touch event has been cancelled
Every time a touch event occurs we are passed a touch event:
const link = document.getElementById('my-link')
link.addEventListener('touchstart', event => {
// touch event started
})
Here are all the properties we can access on that event
identifier
an unique identifier for this specific event. Used to track multi-touch events. Same finger = same identifier.clientX
/clientY
the x and y coordinates of the mouse pointer relative to the browser window, regardless of scrollingscreenX
/screenY
the x and y coordinates of the mouse pointer in the screen coordinatespageX
/pageY
the x and y coordinates of the mouse pointer in the page coordinates (including scrolling)target
the element touched
Event bubbling and event capturing
Bubbling and capturing are the 2 models that events use to propagate.
Suppose you DOM structure is
<div id="container">
<button>Click me</button>
</div>
You want to track when users click on the button, and you have 2 event listeners, one on button
, and one on #container
. Remember, a click on a child element will always propagate to its parents, unless you stop the propagation (see later).
Those event listeners will be called in order, and this order is determined by the event bubbling/capturing model used.
Bubbling means that the event propagates from the item that was clicked (the child) up to all its parent tree, starting from the nearest one.
In our example, the handler on button
will fire before the #container
handler.
Capturing is the opposite: the outer event handlers are fired before the more specific handler, the one on button
.
By default all events bubble.
You can choose to adopt event capturing by applying a third argument to addEventListener, setting it to true
:
document.getElementById('container').addEventListener(
'click',
() => {
//window loaded
},
true
)
Note that first all capturing event handlers are run.
Then all the bubbling event handlers.
The order follows this principle: the DOM goes through all elements starting from the Window object, and goes to find the item that was clicked. While doing so, it calls any event handler associated to the event (capturing phase).
Once it reaches the target, it then repeats the journey up to the parents tree until the Window object, calling again the event handlers (bubbling phase).
Stopping the propagation
An event on a DOM element will be propagated to all its parent elements tree, unless it’s stopped.
<html>
<body>
<section>
<a id="my-link" ...>
A click event on a
will propagate to section
and then body
.
You can stop the propagation by calling the stopPropagation()
method of an Event, usually at the end of the event handler:
const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
// process the event
// ...
event.stopPropagation()
})