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
idofbtn1. 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
formtag, 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
preventDefaultmethod 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
formwith some inputs and anolto 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
inputorselectmust have anameattribute.
- 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
submitevent of theformand notclickevent of thebutton. This is a more generic and robust approach. - As always, we call
preventDefaultto prevent the full page refresh. - We construct a
FormDataobject, which extracts all the input values from the form. - This
FormDataobject is opaque (hides its internals) so we useentriesmethod to extract the input values, and thenObject.fromEntriesto construct a regular JavaScript object. - We perform client-side validation on the submitted
data, and abort if validation fails. - This
dataobject can now be used to send to the server. Or in our case, we just convert it to JSON string and append it aslito 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.
