Events
Events are things that happen all of sudden. Examples of events include: pressing a button on the keyboard, moving your finger on a touch screen, receiving an email, or that five minutes has elapsed.
In Javascript, we can send ("trigger", "emit" or "fire") events, or receive ("listen", "subscribe", "wire-up") events. If we are interested when the user presses a key, we could "listen" for that event, and do something when it happens. Many events come from lower-levels of the system, and the beauty of the event system is that we don't really need to care where they come from. What we care about is the fact that a key is pressed - what part of the system generated the event is (usually) not important. This is called "loose coupling" and makes many kinds of programming more intuitive.
Events can also be used for the internals of code. For example, when you request to fetch data from the network, an event might be triggered when the data has arrived.
Wiring and unwiring events
To get started with events, you need to know the name of the event you care about. Normally you find this out by reading the documentation for the element or API you're working with.
Here, we want to use the "click" event. We then need some way of referring to the source of the event. In most cases, this will correspond to a HTML element - for example the element the user interacted with.
In this case, we want to listen to a particular button in the HTML with an id of "activateButton". Once we find that element using getElementById
, we can call addEventListener
to wire up our event handler, onButtonClicked
.
const onButtonClicked = (e) => {
// This block runs when 'click' happens on 'activateButton'.
}
const el = document.getElementById('activateButton');
el.addEventListener('click', onButtonClicked);
To remove an event, you need a reference to it, which is why we assigned the function to a variable when adding it.
const el = document.getElementById('activateButton');
el.removeEventListener('click', onButtonClicked);
This is easy to do for single items that we know the id for, but it's also pretty straightforward for several items which match a selector (see DOM for more).
The following snippet adds an event listener for every element with the class 'detail':
const elems = document.getElementsByClassName('detail');
for (let el of elems) {
el.addEventListener('click', onButtonClicked);
}
It's also worth saying that you can use anonymous event handlers (to save a little bit of typing) if you won't be needing to remove them later:
document.getElementById('activateButton').addEventListener('click', (e) => {
// This block runs when 'click' happens on 'activateButton'.
});
Another way you'll sometimes see events being wired up is as follows:
el.onclick = (e) => {
// This block runs when 'click' hapens
}
The downside of this approach is that you can only have one event listener and it's not as straightforward to remove an event listener.
Event data
Each time an event occurs, it carries along with it information about the event. It's up to you whether you want to use it. For example, if you're listening to the 'keypress' event, you'll get told what key was pressed, whether the SHIFT key was also held and so on. If you're listening to touch events, you'll find out precisely where the touch happened, etc.
By convention, the event data (or 'event arguments') comes in as the first parameter of your event handler. By convention, that variable is often called 'e' or 'evt':
document.getElementById('activateButton').addEventListener('click', (e) => {
// 'e' contains the juicy event data
console.dir(e); // Find out what's inside
}
It's very instructive to simply dump out the contents of event arguments as the events fire to get a sense of what they contain - usually they are self-descriptive. You can access properties of the event using the standard object dot notation, eg e.pageX
to get the pageX
property of a mouse event argument.
Bubbling
Events that happen as part of the page (DOM) bubble up, meaning that events from children elements flow up the HTML tree unless stopped. This is very useful. If you have a toolbar full of buttons, rather than add event listeners to each button, you could simply add a single event handler to the toolbar itself and then check which button was pressed:
HTML:
<div id="toolbar">
<button value="save">Save</button>
<button value="open">Open</button>
...
</div>
JS:
// Add listener on the toolbar
document.getElementById('toolbar').addEventListener('click', (e) => {
// e.target refers to the element this click event came from
// Ignore clicks on the toolbar itself
if (e.target.id == 'toolbar') return;
switch (e.target.value) {
case "save":
saveDocument();
break;
case "open":
openDocument();
break;
default:
// unknown button
}
}
});
To prevent bubbling, call stopPropagation()
on the event data. This can be necessary when you want to prevent an event handler further up the chain also being executed (eg another 'click' handler on the BODY
element)
A similar concern you may have is wanting to inhibit default browser behaviour. This is quite common if you're handling form submissions too. To do so, call preventDefault()
on the event data.
One-time events
If you want an event to be handled a single time, you can of course add and then remove it with addEventHandler
and removeEventHandler
.
Another way of achieving this is by passing configuration options to addEventListener
, and in particular the once
field:
document.getElementById("activateButton").addEventListener('click', function(e) {
// This block runs when 'click' happens on 'activateButton'
// If the click happens again, the function won't fire
}, { once: true });
After a few more concepts are introduced, we'll cover how to use common events for interactivity such as key presses and touches.
Read more
- Eloquent JavaScript Events