Any websites with dynamic functionality need to deal with users interacting with them. This is the pages have controls that users can use. To let users use those controls, the pages have to be able to handle the actions done by the users to the controls. These are example of events in JavaScript. Events also include other activities done by the browser like page loading.
With the HTML DOM, JavaScript can get the elements from there and handle events. Some events are divided into groups to what elements they’re applied to. Other events apply to all elements other than the body and frameset elements.
The list of events that are supported by all HTML elements are below:
abort
, triggered when file load is abortedchange
, triggered when value has changedclick
, triggered when mouse is clicked or screen is tappeddbclick
, triggered when mouse is clicked twiceinput
, trigger when input value in aninput
ortextarea
element is changedkeydown
, triggered when key is downkeyup
, trigger when key is released after being pressedmousedown
, triggered when mouse button is downmouseenter
, triggered when mouse button is releasedmouseleave
, triggered when mouse leaves over an elementmousemove
, triggered when mouse moves over an elementmouseout
, triggered when mouse moves off an elementmouseover
, triggered when mouse hovers over an elementmouseup
, triggered when mouse button is releasedmousewheel
, triggered when mouse wheel is rotatedonreset
, triggered when a form is resetselect
, triggered when text is selectedsubmit
, triggered when form is submitteddrag
, triggered when element is being draggeddragend
, triggered when element is draggeddragenter
, triggered when dragged element enters the drop targetdragstart
, triggered when element starts draggingdragleave
, triggered when dragged element leaves a valid drop targetdragover
, triggered when element or text selection is dragged over a valid drop targetdrop
, triggered when element is dropped on a valid drop target
Below some HTML audio and video events:
pause
, triggered when playback of media has startedplay
, triggered when playback has begunplaying
, triggered when media is playingseeking
, triggered when media is being seekedseeked
, triggered when media is done seeking
Events supported by every element except the body
and frameset
elements are below:
blur
, triggered when element loses focuserror
, triggered when file failed to loadfocus
, triggered when element is in focusload
, triggered when a file and attached files are loadingresize
, triggered when document is resizedscroll
, triggered when element is scrolled
Events supported by the window
object is below:
afterprint
, triggered when print preview window has closed or document has started printingbeforeprint
, triggered when print preview window is opened or document is going to printbeforeunload
, triggered when the document is unloadedhashchange
, triggered when the part of the URL with the pound sign (#) is changedpagehide
, triggered when browser leaves a page in browser historypageshow
, triggered when browser goes to a pagepopstate
, triggered when session history item changesunload
, triggered when document and included files are being unloaded
There are many more events that can be handled by browsers. They are listed at https://developer.mozilla.org/en-US/docs/Web/Events.
Event Handling
When the browser’s JavaScript code is responding to those events, it’s called event handling. There are a few ways to handle events in JavaScript.
Inline Event Handlers
We can add event handlers straight to an element’s code. We can write code in HTML element’s attributes to handle code. For example, if we want to handle the event when a button is clicked we can put the event handling code in the onclick
attribute of the button element, like so:
<button onclick='alert('Button clicked')>Click me</button>
It’s likely than we want to prevent the default action from happening, like navigating to a new page or triggering submit when a button is clicked. To prevent these default actions from happening, we can put return false
at the end of the script. We can do following to prevent navigation to a new page if we handle the onclick
event in an a
tag:
<a onclick='alert('Clicked'); return false;'>Click me</a>
Handling Events in Script Tag
Inline event handlers aren’t good because you’re mixing dynamic code in HTML, which is supposed to only care about organizing pages into meaningful sections. Also, if you have complex code, then it’s very hard to put all the code in the attribute. We should attach event handlers to elements by getting them in dedicated code and then putting the code in the event handler’s callback function to handle the events.
For example, if we have code that increases and decrease the counter when you press different buttons, we can write the code as follows.
We make a new folder, then in index.html
, we put:
<html>
<head>
<title>Counting App</title>
</head>
<body>
<h1>Click the button to count.</h1>
<p>Current Number: <span id="currentCount">0</span></p>
<button id="increaseButton">Increase</button>
<button id="decreaseButton">Decrease</button>
<script src="script.js"></script>
</body>
</html>
Then in script.js
, we put:
const handleIncrease = () => {
const currentCount = document.getElementById("currentCount");
const increaseButton = document.getElementById("increaseButton");
increaseButton.onclick = () => {
currentCount.innerHTML++;
};
};
const handleDecrease = () => {
const currentCount = document.getElementById("currentCount");
const decreaseButton = document.getElementById("decreaseButton");
decreaseButton.onclick = () => {
currentCount.innerHTML--;
};
};
const initialize = () => {
handleIncrease();
handleDecrease();
};
window.onload = initialize;
In script.js
, we have the functions to handle the clicks of the buttons in index.html
. We attached the event handlers by getting the element object by using getElementById
, then we set the onclick
property of the elements with their own anonymous event handler functions. If we want to assign a function reference to it, we can do it like we did to window.onload
. In the handlers we get the content of the element with IDcurrentCount
, and then modified the innerHTML
property to change the content after we got the original content. In the last line, we assign the initialize
variable to it. We do not want to call it immediately, but rather when the event actually happened, so there’s no parenthesis after the initialize
. We are assigning it the reference on the function, rather than calling it immediately and returning something then assigning the result to it.
addEventListener
Another way to attach an event handler to a function is to use the addEventListener
function available with the HTML element object. The addEventListener
event listens to DOM events that’s trigger and runs the callback function that you pass into it when the event happens. The callback function has one argument which contains the details of the event that was triggered. With addEventListener
we can reuse code since we can use the same callback function in multiple places. You can also control how the event is triggered since we have the parameter which contains the event details. It also works on non-element nodes in the DOM tree.
If we change the above example to use addEventListener
we can replace what we had in script.js
with:
const handleClick = e => {
const currentCount = document.getElementById("currentCount");
if (e.target.id === "increaseButton") {
currentCount.innerHTML++;
} if (e.target.id === "decreaseButton") {
currentCount.innerHTML--;
}
};
const handleIncrease = () => {
const increaseButton = document.getElementById("increaseButton");
increaseButton.addEventListener("click", handleClick);
};
const handleDecrease = () => {
const decreaseButton = document.getElementById("decreaseButton");
decreaseButton.addEventListener("click", handleClick);
};
const initialize = () => {
handleIncrease();
handleDecrease();
};
window.onload = initialize;
As we can see, we used the same handleClick
function for handling clicks of button the increaseButton
and the decreaseButton
. We did this by checking which element is triggering the click event by using the e.target.id
property, which has the ID attribute of the element. Doing different actions in the same event handler function for different elements is called event delegation. We are delegating different actions as different elements are triggered. There’s less repeated code than the first example.
The addEventListener
function takes 3 arguments. The first is the string that has the event name, the second is a callback function to handle the events. The last one indicates whether a parent element is also associated with the event. It’s true
if it is and false
otherwise.
If there’s a parent element that’s also associated with the event, then you can propagate the event to the parent element. It’s called bubbling up an event and the event will also be triggered on the parent element if it does. There’s also the capture method, which lets the parent element’s event happen first and the child after. It’s rarely used in code so we don’t have to be too concerned about this.
An example of event bubbling up would be as follows. If we have the following in index.html
:
<html>
<head>
<title>Click App</title>
</head>
<body>
<div id="grandparent">
<div id="parent">
<button id="child">
Click Me
</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
and the following in script.js
:
window.onload = () => {
const child = document.getElementById("child");
const parent = document.getElementById("parent");
const grandparent = document.getElementById("grandparent");
child.addEventListener(
"click",
() => {
alert("Child Clicked");
},
true
);
parent.addEventListener(
"click",
() => {
alert("Parent Clicked");
},
true
);
grandparent.addEventListener(
"click",
() => {
alert("Grandparent Clicked");
},
true
);
};
Then when the button ‘Click me’ is clicked, we should get 3 alerts, since we specified to let the click events bubble up all the way to the top element.
Stop Event Propagation
If we only want the event to happen on the one you want, then you have to stop it from propagating to the parent. If we modify script.js
from the previous example to:
window.onload = () => {
const child = document.getElementById("child");
const parent = document.getElementById("parent");
const grandparent = document.getElementById("grandparent");
child.addEventListener(
"click",
e => {
if (e.stopPropagation) {
e.stopPropagation();
}
alert("Child Clicked");
},
true
);
parent.addEventListener(
"click",
e => {
if (e.stopPropagation) {
e.stopPropagation();
}
alert("Parent Clicked");
},
true
);
grandparent.addEventListener(
"click",
e => {
if (e.stopPropagation) {
e.stopPropagation();
}
alert("Grandparent Clicked");
},
true
);
};
We should only see ‘Grandparent Clicked’ since we stopped the event from propagating with the stopPropagation
function. Stopping propagation should make your code faster since bubbling and capturing takes resources.