The DOM in a web page generates a multitude of events (e.g. click
, keydown
, load
, etc.) for web apps to respond to. The developer can write JavaScript functions (i.e. "callbacks") and attach them to DOM events such that the callback runs every time that event fires.
Simple Event Callback
Here's a simple example:
- Create a button somewhere on your page with
id
ofbtn1
. Make sure it is a standalone button, and not a part of aform
. - Add the following JavaScript code to the page. It must run after the button is loaded into the page.
const btn1 = document.getElementById("btn1"); // there must be a button on the page with this id
let counter = 0; // a counter to count clicks
btn1.addEventListener("click", function(event) { // the callback gets the DOM `event` as parameter
const target = event.target; // the DOM element that is the "target" of the event (button that was clicked)
counter++; // increment the counter
const message = `You clicked ${target.id} ${counter} time${counter>1 ? 's' : ''}`;
target.innerText = message; // update DOM
}); - Test it. It should work.
- Now wrap the button in a
form
tag, like this:<form>
<button id="btn1">Click Me!</button>
</form> - Test it again. This time you should see something strange. The button text does change for a brief moment, but then goes back to the original ("Click Me!") soon after. Why?
- The reason is that since this button is a part of a
form
, right after executing your callback, it perform the button's default action, which is to submit the form that it is a part of. And since the form is getting submitted, the whole page refreshes, and thus the button text goes back to the original. - To fix this, all you have to do is to prevent the default action of the button from running. This can be accomplished with
preventDefault
method of the fired event. Call that method on the event before returning, by adding the following line to the beginning of the callback function.btn1.addEventListener("click", function(event) { // the callback gets the DOM `event` as parameter
event.preventDefault(); // don't perform the default action of this event
// ... code to respond to the event ...
}); - Test it again. This time it should work as intended.
Form Submit Example
In modern JavaScript apps, it is very common to convert a traditional form
from submitting normally (and refreshing the whole page) to intercepting the submission, performing client-side validations, and then sending form inputs to server using AJAX and handling the response, all without refreshing the page. So let's give that a try.
- Create a
form
with some inputs and anol
to show the submissions. Feel free to change the inputs from what is shown below.<!-- input -->
<form id="test-form">
<input name="firstName" type="text" placeholder="First Name" required>
<input name="lastName" type="text" placeholder="Last Name" required>
<input name="dob" type="date" placeholder="Date of Birth">
<select name="employment" required>
<option value="">Employment Status</option>
<option>Salaried</option>
<option>Self-Employed</option>
<option>Retired</option>
<option>Unemployed</option>
</select>
<div>
<button>Submit</button>
<button type="reset">Reset</button>
</div>
</form>
<!-- output -->
<fieldset>
<legend>Submissions</legend>
<ol id="submission-list">
<!-- submissions will appear here -->
</ol>
</fieldset> - Note that ...
- We gave id's to the elements that we'll need to manipulate from JavaScript code.
- In order to be considered a part of the form-data, an
input
orselect
must have aname
attribute.
- Now, include the following JavaScript code somewhere below the above HTML.
const submissions = document.getElementById("submission-list"); // grab the output area
document.getElementById("test-form").addEventListener("submit", function(e) { // grab form and attach listener
e.preventDefault(); // prevent submission (default action)
const form = e.target;
const formData = new FormData(form); // construct FormData from form
const data = Object.fromEntries(formData.entries()); // construct a regular JS object from formData
// client-side validation
if(data.firstName.length < 3 || data.lastName.length < 3) {
alert("First Name and Last Name must be at least 3 characters long");
return; // Validation failed. Abort.
}
const li = document.createElement("LI");
li.innerText = JSON.stringify(data); // convert `data` object to JSON string and display it
submissions.append(li);
}); - Note that ...
- We are attaching to the
submit
event of theform
and notclick
event of thebutton
. This is a more generic and robust approach. - As always, we call
preventDefault
to prevent the full page refresh. - We construct a
FormData
object, which extracts all the input values from the form. - This
FormData
object is opaque (hides its internals) so we useentries
method to extract the input values, and thenObject.fromEntries
to construct a regular JavaScript object. - We perform client-side validation on the submitted
data
, and abort if validation fails. - This
data
object can now be used to send to the server. Or in our case, we just convert it to JSON string and append it asli
to the pre-existingol
.
- We are attaching to the
- There are some other advanced concepts such as event bubbling that we haven't covered here. But you should learn more about them.