JavaScript/Handling DOM events
Applications with a user interface - and other application types - are predominantly driven by events. Here we focus on DOM events. They originate (or fire) from specific actions or situations in a browser or a native app, e.g., a user clicks with the mouse, types a keyboard key, or works on a touch screen; a visual object is 'dragged & dropped', 'copied & pasted', or resized; the HTML page is loaded or shall be unloaded; the browser or a visual object gets or loses the focus; and much more. It's also possible that the application creates events programmatically (dispatch).
An event is related to its originating object and with a JavaScript statement; that is, in most cases, a call to a function that is denoted as the event handler. The JavaScript part is invoked after the event arises. Common actions are communication with a server, validation of user input, or modification of DOM or graphics.
Create and invoke events
[edit | edit source]Embedded in HTML
[edit | edit source]A short example shows how events are defined in an HTML page, fire, and execute. This syntax version is denoted as inline JavaScript.
<!DOCTYPE html>
<html>
<head>
<script>
function handleEvent(evt) {
"use strict";
alert("Perform some actions via a JavaScript function.");
}
</script>
</head>
<body id="body" style="margin:2em">
<!-- inline all statements; unusual -->
<button onclick="const msg='Direct invocation of small actions.'; alert(msg);">First button</button>
<!-- call a function (the event handler) -->
<button onclick="handleEvent(event)">Second button</button>
</body>
</html>
When a user clicks on one of the buttons, the browser reads the button's attribute onclick
, creates a JavaScript object that contains all properties of the event, and executes the JavaScript statement(s). Mostly, the JavaScript part is a call to a function. That function is denoted the event handler. It receives the JavaScript object as its first parameter. Only in very simple cases, the complete JavaScript script is inlined.
Of course, the called function must exist somewhere. Some standard functions like alert()
are predefined and provided by the browser. Your self-written functions exist within the HTML tag <script>
, or the tag refers to a file or URL where they exist.
Hint: Embedding event definitions directly into HTML is called inline JavaScript. It's the earliest method of registering event handlers, but it tends to make the source hard to read for non-trivial applications. Therefore it can be seen as being a less desirable technique than other unobtrusive techniques; see next chapter. The use of inline JavaScript can be considered to be similar in nature to that of using inline CSS, where HTML is styled by putting CSS in style
attributes.
Nevertheless, the Wikibook on hand will use inline JavaScript often in its demonstration pages because the syntax is short and the concept is easy to understand.
Programmatically in JavaScript
[edit | edit source]JavaScript knows two ways to register an event handler for an HTML element. First, the event handler function can be directly assigned to the element's properties onxxx
(onclick, onkeydown, onload, onfocus, ...). Their name starts with 'on' and ends with the value of the event type. Second, the function addEventListener
registers the event type and the event handler.
<!DOCTYPE html>
<html>
<head>
<script>
function register() {
"use strict";
const p1 = document.getElementById("p1");
// do it this way ...
p1.onclick = showMessage; // no parenthesis ()
// ... or this way (prefered)
//p1.addEventListener('click', showMessage);
alert("The event handler is assigned to the paragraph.");
}
function showMessage(evt) {
"use strict";
// the parameter 'evt' contains many information about
// the event, e.g., the position from where it originates
alert("A click-event to the paragraph. At position " +
evt.clientX + " / " + evt.clientY + ". Event type is: " + evt.type);
}
</script>
</head>
<body>
<h1>Register an event</h1>
<p id="p1" style="margin:2em; background-color:aqua">
A small paragraph. First without, then with an event handler.
</p>
<button onclick="register()" id="button_1">A button</button>
</body>
</html>
The onclick event handler 'register' of button 'button_1' is registered with the above inline JavaScript syntax. When the page loads, only this event handler is known. Clicks to the paragraph 'p1' don't trigger any action because it does not have any event handler so far. But when the button gets pressed, the handler of button 'button_1' registers the second event handler, the function 'showMessage'. Now, after a click on the paragraph, the alert "A click-event to the paragraph..." occurs.
The registration is done in line 10 p1.onclick = showMessage
. The noticeable difference to the inline JavaScript is that there are no parenthesizes. The inline JavaScript calls the function showMessage
and hence needs to use parenthesizes. The function register
does NOT call showMessage
. It uses only its name for the registration process.
The alternative to assigning the function to the paragraph's 'onclick' property is the use of the function addEventListener
. It acts on the element 'p1' and takes two parameters. The first one is the event type (click, keydown, ...). Such event types correlate with the onxxx names in that they miss the first two characters 'on'. The second parameter is the name of the function that acts as the event handler.
You can test the example by commenting out either line 10 or line 12. Line 12 with the addEventListener
function is the preferred version.
Event types
[edit | edit source]Different kinds of events exist depending on the kind of the originating element. The complete list of event types is incredibly huge (MDN) (W3schools). We show some important types and examples.
Name | Description |
---|---|
blur
|
An input element loses focus |
change
|
An element gets modified |
click
|
An element gets clicked |
dblclick
|
An element gets double-clicked |
error
|
An error occurred loading an element |
focus
|
An input element received focus |
keydown
|
A key was pressed when an element had focus |
keyup
|
A key was released when the element had focus |
load
|
An element was loaded |
mouseenter
|
The mouse pointer was moved into the element |
mousemove
|
The mouse pointer moves while inside the element |
mousedown
|
The mouse button was pressed on the element |
mouseup
|
The mouse button was released on the element |
mouseleave
|
The mouse pointer was moved out of the element |
reset
|
The form's reset button was clicked |
resize
|
The containing window or frame was resized |
select
|
Some text within the element was selected |
submit
|
A form is being submitted |
unload
|
The content is being unloaded (e.g., window being closed) |
The following example demonstrates some different event types.
<!DOCTYPE html>
<html>
<head>
<script>
function registerAllEvents() {
"use strict";
// register different event types
document.getElementById("p1").addEventListener("click", handleAllEvents);
document.getElementById("p2").addEventListener("dblclick", handleAllEvents);
document.getElementById("p3").addEventListener("mouseover", handleAllEvents);
document.getElementById("t1").addEventListener("keydown", handleAllEvents);
document.getElementById("t2").addEventListener("select", handleAllEvents);
document.getElementById("button_1").addEventListener("mouseover", handleAllEvents);
}
function handleAllEvents(evt) {
"use strict";
alert("An event occurred from element: " +
evt.target.id + ". Event type is: " + evt.type);
}
</script>
</head>
<body onload="registerAllEvents()" style="margin:1em">
<h1 id="h1" style="background-color:aqua">Check Events</h1>
<p id="p1" style="background-color:aqua">A small paragraph. (click)</p>
<p id="p2" style="background-color:aqua">A small paragraph. (double click)</p>
<p id="p3" style="background-color:aqua">A small paragraph. (mouse over)</p>
<p style="background-color:aqua">
<textarea id="t1" rows="1" cols="50">(key down)</textarea>
</p>
<p style="background-color:aqua">
<textarea id="t2" rows="1" cols="50">(select)</textarea>
</p>
<button id="button_1">A button (mouse over)</button>
</body>
</html>
When the page is loaded, the onload event of the body
is fired. Please notice that here the 'on' prefix is necessary because it's the inline JavaScript syntax (line 23). The called function registerAllEvents locates diverse HTML elements and registers event handlers of different types (lines 8 - 13). Often you will register different functions, but to keep things easy, we register in this example the same function handleAllEvents to all elements. This function reports the event type and the originating HTML element.
Event properties
[edit | edit source]The event is always passed to the event handler as its first parameter in the form of a JavaScript object. JavaScript objects consist of properties; properties are key/value pairs. In all cases, one of the keys is 'type'. It contains the event's type; some of its possible values are shown in the above table. Depending on the event type, a lot of other properties are available.
Here are some examples of more or less important properties.
Name | Description |
---|---|
button |
Returns which mouse button was clicked |
clientX |
Returns the horizontal coordinate of the mouse pointer within the local coordinates: scrolled-out parts don't count |
clientY |
Returns the vertical coordinate of the mouse pointer within the local coordinates: scrolled-out parts don't count |
code |
Returns a textual representation of the pressed key, e.g., "ShiftLeft" or "KeyE" |
key |
Returns the value of the pressed key, e.g., "E" |
offsetX |
Returns the horizontal coordinate of the mouse pointer within the target DOM element |
offsetY |
Returns the vertical coordinate of the mouse pointer within the target DOM element |
pageX |
Returns the horizontal coordinate of the mouse pointer within the page coordinates - including scrolled-out parts |
pageY |
Returns the vertical coordinate of the mouse pointer within the page coordinates - including scrolled-out parts |
screenX |
Returns the horizontal coordinate of the mouse pointer within the complete monitor coordinates |
screenY |
Returns the vertical coordinate of the mouse pointer within the complete monitor coordinates |
target |
Returns the element that triggered the event |
timeStamp |
Returns the number of milliseconds between element creation and event creation |
type |
Returns the type of the element that triggered the event |
x |
An alias for clientX |
y |
An alias for clientY |
An example of accessing a property is given in the following script.
<!DOCTYPE html>
<html>
<head>
<script>
function changeTitle(evt) {
"use strict";
const xPos = evt.x;
const yPos = evt.y;
document.title = [xPos, yPos];
}
</script>
</head>
<body onmousemove="changeTitle(event)" style="margin:1em; border-width: 1px; border-style: solid;">
<h1 id="h1" style="background-color:aqua">Check Events</h1>
<p id="p1" style="margin:2em; background-color:aqua">A small paragraph.</p>
<p id="p2" style="margin:2em; background-color:aqua">Another small paragraph.</p>
<button id="button_1">Button</button>
</body>
</html>
A mouse-move event is registered for the body
. Whenever the mouse moves across the body, the event is triggered. The event handler reads the x/y properties out of the JavaScript object and shows them in the title of the browser's active tab.
removeEventListener
[edit | edit source]Similar to addEventListener
the function removeEventListener
removes an event listener from an element.
Synthetic events
[edit | edit source]The system offers the above-shown rich set of event types. Additionally, you can create your own events and trigger them from your application.
First, you create a function with one parameter, the event handler. Next, you register this event handler for an element. So far, everything is the same as with predefined event types.
function register() {
"use strict";
// ...
// choose an arbitrary event type (first parameter of 'addEventListener')
// and register function on element
document.getElementById("p1").addEventListener("syntheticEvent", f);
}
// the event handler for the non-system event
function f(evt) {
alert("Invocation of the synthetic event on: " + evt.target.id +
" The event type is: " + evt.type);
}
Now you can trigger this event in any part of your application. To do so, you create a new event of precisely the chosen type and fire it with a call to dispatchEvent
.
function triggerEvent(evt) {
"use strict";
// create a new event with the appropriate type
const newEvent = new Event("syntheticEvent");
// trigger this event on element 'p1'
document.getElementById("p1").dispatchEvent(newEvent);
}
For test purposes, we bind this functionality to the button. The complete page now looks like this:
<!DOCTYPE html>
<html>
<head>
<script>
function register() {
"use strict";
document.getElementById("p1").addEventListener("click", showMessage);
document.getElementById("p2").addEventListener("click", showMessage);
document.getElementById("button_1").addEventListener("click", triggerEvent);
// choose an arbitrary event type (first parameter of 'addEventListener')
// and register function on element
document.getElementById("p1").addEventListener("syntheticEvent", f);
}
// the event handler for the non-system event
function f(evt) {
"use strict";
alert("Invocation of the synthetic event on: " + evt.target.id +
" The event type is: " + evt.type);
}
function showMessage(evt) {
"use strict";
// the parameter 'evt' contains many information about
// the event, e.g., the position from where it originates
alert("A click event to element: " + evt.target.id +
" The event type is: " + evt.type);
}
function triggerEvent(evt) {
"use strict";
// create a new event with the appropriate type
const newEvent = new Event("syntheticEvent");
// trigger this event on element 'p1'
document.getElementById("p1").dispatchEvent(newEvent);
}
</script>
</head>
<body onload="register()">
<h1>Create an event programmatically.</h1>
<p id="p1" style="margin:2em; background-color:aqua">A small paragraph.</p>
<p id="p2" style="margin:2em; background-color:aqua">Another small paragraph.</p>
<button id="button_1">Button</button>
</body>
</html>
At the beginning, the button listens to click events, and 'p1' listens to events of type 'click' as well as of type 'syntheticEvent'. When the button is clicked, his event handler 'triggerEvent' creates a new event of type 'syntheticEvent' and fires it on 'p1' (what is the primary purpose of this example). The event handler showMessage
shows a message without 'p1' being clicked. In other words: The event on 'p1' occurs without a click on 'p1'.
Possibly you need in the event handler some data from the calling function, e.g., the text of an error-message, the data of an HTTP response, ... . You can pass such data by using the CustonEvent
and its property 'detail':
const newEvent = new CustomEvent("syntheticEvent", {detail: "A short message."});
Access the data in the event handle:
function f(evt) {
"use strict";
alert("Invocation of the synthetic event on: " + evt.target.id +
" The event type is: " + evt.type + ". " + evt.detail);
}
(A)synchronous behaviour
[edit | edit source]Most events are handled synchronously, e.g., 'click', 'key', or 'mouse'. But there are a few exceptions that are handled asynchronously, e.g., 'load' [1] [2]. 'Synchronous' means that the sequence of invocations is the same as the sequence of their creations. Clicking on Button A, B, and then C leads to the invocation of A's, B's, and then C's event handler in exactly this sequence. In contrast, 'asynchronous' events can lead to the invocation of the correlated handlers in an arbitrary sequence.
You must distinguish this question, the invocation of event handlers, from the implementation of their bodies. Every implementation may act strictly sequential or may contain asynchronous calls - it depends on your intention. Typical asynchronous calls are HTTP requests or database queries. Of course, they can be part of the handler of a click event.