JavaScript Events: click, input, submit, mouse and keyboard
Learn what events are and how to react to clicks, typing, form submits, mouse moves and key presses in JavaScript
What you will learn
A web page that never reacts is just a poster. Real pages respond to the user: a click opens a menu, typing filters a list, a form is checked before it is sent, a key press moves a game character. All of these are events. In this lesson you will learn what events are, three ways to attach code to them, and the most important event types: click, input, submit, mouse, keyboard and form events. You will build small, real features with each.
What is an event?
An event is a signal from the browser that something happened: the user clicked, a key went down, a form was sent, the page finished loading. The browser creates these signals all the time. Your job is to say: “When this happens to this element, run this function.”
Think of a doorbell. The event is the bell ringing. The event listener is you, waiting at home. The handler (or callback) is what you do when it rings: open the door. If nobody is listening, the bell rings and nothing happens.
JavaScript in the browser is event-driven: most of your code sits waiting and runs only when an event arrives.
Every event has three parts:
| Part | Question | Example |
|---|---|---|
| Target | On which element did it happen? | The “Join” button |
| Type | What happened? | "click" |
| Handler | What should run? | A function that shows a message |
Three ways to attach a handler
1. Inline HTML attribute (avoid)
<button onclick="alert('Hello!')">Say hello</button>
It works, but it mixes JavaScript into HTML, gets messy fast, and only allows simple cases. Real projects avoid it. You will still see it in old tutorials.
2. DOM property (onclick)
const button = document.getElementById("counter");
button.onclick = function () {
console.log("Clicked!");
};
Simple, but there is only one onclick slot. A second assignment replaces the first:
button.onclick = function () { console.log("A"); };
button.onclick = function () { console.log("B"); }; // A is gone. Only B runs.
3. addEventListener (use this)
button.addEventListener("click", function () {
console.log("Clicked!");
});
You can add as many listeners as you like to the same element and event, and they all run, in the order you added them:
button.addEventListener("click", function () { console.log("A"); });
button.addEventListener("click", function () { console.log("B"); }); // click prints A, then B
This is the modern and recommended way. The next lessons cover it deeply (removeEventListener, options like once, and more). In this lesson, we will use it in its basic form:
element.addEventListener("eventName", handlerFunction);
Inline onclick="" |
el.onclick = fn |
addEventListener |
|
|---|---|---|---|
| JavaScript stays out of HTML | No | Yes | Yes |
| Many handlers on one event | No | No | Yes |
| Can be removed later | Hard | Set to null |
removeEventListener |
| Modern projects | Avoid | Rare | Standard |
Pass the function, do not call it
The handler must be a function reference. If you add parentheses, you call it right now, instead of when the event happens:
function sayHi() {
console.log("Hi!");
}
button.addEventListener("click", sayHi); // correct: runs on click
button.addEventListener("click", sayHi()); // wrong: runs immediately, and passes undefined
If you need to pass your own arguments, wrap the call in another function:
button.addEventListener("click", function () {
greet("Riya");
});
button.addEventListener("click", () => greet("Riya")); // arrow function version
The sample page
The examples below use this page, also loaded in Practice in Editor:
<h1>Events Practice</h1>
<button id="counter">Clicked 0 times</button>
<p>
<input id="name" type="text" placeholder="Type your name">
<span id="greeting"></span>
</p>
<div id="box" class="box">Hover, click and move your mouse here</div>
<p id="mouse-info">Mouse: -</p>
<p id="key-info">Press any key</p>
<form id="form">
<input id="email" type="email" placeholder="Your email">
<select id="plan">
<option value="free">Free</option>
<option value="pro">Pro</option>
</select>
<button type="submit">Join</button>
</form>
<p id="form-info"></p>
.box {
width: 280px;
padding: 30px;
text-align: center;
border: 2px solid #94a3b8;
border-radius: 8px;
user-select: none;
}
.box.hover {
background: #dbeafe;
}
Mouse events
| Event | When it fires |
|---|---|
click |
The user presses and releases the main button (also for Enter/Space on a focused button) |
dblclick |
Two quick clicks |
contextmenu |
Right-click (the context menu is about to open) |
mousedown / mouseup |
The button goes down / comes up |
mouseenter / mouseleave |
The pointer enters / leaves the element |
mouseover / mouseout |
Like the above, but they also fire again when the pointer moves between child elements |
mousemove |
The pointer moves over the element (fires very often) |
The click counter
The “Hello World” of events:
const counter = document.getElementById("counter");
let clicks = 0;
counter.addEventListener("click", function () {
clicks++;
counter.textContent = "Clicked " + clicks + " times";
});
Hover effects
const box = document.getElementById("box");
box.addEventListener("mouseenter", function () {
box.classList.add("hover");
});
box.addEventListener("mouseleave", function () {
box.classList.remove("hover");
});
(For a simple colour change like this, CSS :hover is better. Use JavaScript when the hover must do more, such as show a popup or load data.)
mouseenter vs mouseover
Use mouseenter and mouseleave for normal hover effects. mouseover and mouseout repeat when the pointer crosses into a child element, which causes flickering effects. Choose the first pair unless you have a special reason.
Mouse position
Mouse events give you an event object with the coordinates. The event object is the topic of the next lesson, so here is just a taste:
const mouseInfo = document.getElementById("mouse-info");
box.addEventListener("mousemove", function (event) {
mouseInfo.textContent = "Mouse: x=" + event.offsetX + ", y=" + event.offsetY;
});
The browser passes the event object as the first argument of your handler. You may name it event, e or evt.
Tip:
mousemovecan fire many times per second. Keep its code light. A later lesson on debounce and throttle shows how to slow it down.
Keyboard events
| Event | When it fires |
|---|---|
keydown |
A key goes down. It repeats while the key is held |
keyup |
A key comes up |
keypress |
Old and deprecated. Do not use it. Use keydown |
document.addEventListener("keydown", function (event) {
document.getElementById("key-info").textContent = "You pressed: " + event.key;
});
Here we listen on document, so keys are caught wherever the focus is. Listen on an <input> instead to catch only keys typed in that input.
event.key is the character or name of the key: "a", "A", "5", "Enter", "Escape", " " (space), "ArrowUp". The next lesson covers key vs code in detail.
Do something on a specific key
const messageInput = document.getElementById("name");
messageInput.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
console.log("Send:", messageInput.value);
}
if (event.key === "Escape") {
messageInput.value = "";
}
});
Form and input events
Forms have the most events, and they are used in almost every real website.
| Event | On | When it fires |
|---|---|---|
input |
<input>, <textarea>, <select> |
Every time the value changes, instantly (each key, paste, delete) |
change |
Form controls | When the change is finished: a text input when it loses focus, a select/checkbox/radio at once |
focus / blur |
Form controls | The element gets / loses focus (they do not bubble) |
focusin / focusout |
Any element | Like focus and blur, but they bubble |
submit |
<form> |
The form is being sent (Join button or Enter key) |
reset |
<form> |
The form is reset |
input: react while typing
const nameInput = document.getElementById("name");
nameInput.addEventListener("input", function () {
document.getElementById("greeting").textContent = "Hello, " + nameInput.value + "!";
});
input vs change
For a text box, input fires on every key and change fires only when you leave the box (or press Enter). For a dropdown, checkbox or radio, both fire when you pick an option.
const plan = document.getElementById("plan");
plan.addEventListener("change", function () {
console.log("Selected plan:", plan.value);
});
| I want to… | Use |
|---|---|
| Update live while typing (search, counter, preview) | input |
| React to a finished choice (dropdown, checkbox, date) | change |
| Validate when the user leaves a field | blur or change |
focus and blur
const email = document.getElementById("email");
email.addEventListener("focus", function () {
email.style.outline = "2px solid #2563eb";
});
email.addEventListener("blur", function () {
email.style.outline = "";
if (!email.value.includes("@")) {
console.log("Please enter a valid email");
}
});
submit: handle the whole form
A form is submitted when the user clicks a submit button or presses Enter in a field. Always listen for submit on the form, not for click on the button, or the Enter key will skip your code.
By default, the browser sends the form and reloads the page. To handle it yourself with JavaScript, call event.preventDefault() (the next lessons explain this fully):
const form = document.getElementById("form");
form.addEventListener("submit", function (event) {
event.preventDefault(); // stop the page reload
const email = document.getElementById("email").value;
document.getElementById("form-info").textContent = "Submitted: " + email;
});
Tip: A
<button>inside a form is a submit button by default. If you want a button that does something else, such as “Show password”, writetype="button"so it does not submit the form.
Page events
Not every event comes from the user:
| Event | On | When it fires |
|---|---|---|
DOMContentLoaded |
document |
The HTML is read and the DOM is ready |
load |
window |
The page and all images and files finished loading |
resize |
window |
The browser window size changes |
scroll |
window or a scrollable element |
The user scrolls |
document.addEventListener("DOMContentLoaded", function () {
console.log("The DOM is ready");
});
window.addEventListener("resize", function () {
console.log("Width now:", window.innerWidth);
});
Real use cases
1. Live search filter
Type in the box and the list shows only the matching items. This uses input, toLowerCase, includes and a hidden toggle.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Live Search</title>
<style>
body { font-family: sans-serif; padding: 20px; max-width: 360px; }
input { width: 100%; padding: 8px; font-size: 16px; box-sizing: border-box; }
ul { padding-left: 20px; }
li { padding: 4px 0; }
</style>
</head>
<body>
<label for="search">Search a language</label>
<input id="search" type="text" placeholder="Type to filter...">
<ul id="languages">
<li>JavaScript</li>
<li>Python</li>
<li>Java</li>
<li>TypeScript</li>
<li>C++</li>
<li>Rust</li>
</ul>
<p id="none" hidden>No match found.</p>
<script>
const search = document.getElementById("search");
const items = document.querySelectorAll("#languages li");
const none = document.getElementById("none");
search.addEventListener("input", function () {
const query = search.value.trim().toLowerCase();
let visible = 0;
items.forEach(function (item) {
const match = item.textContent.toLowerCase().includes(query);
item.hidden = !match;
if (match) visible++;
});
none.hidden = visible > 0;
});
</script>
</body>
</html>
2. Keyboard shortcut
Many apps use shortcuts like Ctrl + K for search or Escape to close a popup.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shortcuts</title>
<style>
body { font-family: sans-serif; padding: 20px; }
input { padding: 8px; font-size: 16px; width: 260px; }
#modal { display: none; margin-top: 16px; padding: 16px; border: 2px solid #2563eb; border-radius: 8px; max-width: 260px; }
#modal.open { display: block; }
</style>
</head>
<body>
<p>Press <kbd>Ctrl</kbd> + <kbd>K</kbd> to focus the search. Press <kbd>M</kbd> to open the box and <kbd>Esc</kbd> to close it.</p>
<input id="search" type="text" placeholder="Search...">
<div id="modal">I am a popup box.</div>
<script>
const search = document.getElementById("search");
const modal = document.getElementById("modal");
document.addEventListener("keydown", function (event) {
if (event.ctrlKey && event.key.toLowerCase() === "k") {
event.preventDefault(); // stop the browser's own shortcut
search.focus();
}
// ignore single letters while the user is typing in the input
if (document.activeElement === search) return;
if (event.key.toLowerCase() === "m") modal.classList.add("open");
if (event.key === "Escape") modal.classList.remove("open");
});
</script>
</body>
</html>
3. Contact form with validation
A realistic form: check on blur for each field, and again on submit. Error messages use textContent, and the input gets a class (from the classList lesson).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Contact Form</title>
<style>
body { font-family: sans-serif; padding: 20px; max-width: 360px; }
label { display: block; margin-top: 12px; }
input, textarea { width: 100%; padding: 8px; font-size: 16px; box-sizing: border-box; border: 2px solid #d1d5db; border-radius: 6px; }
.invalid { border-color: #dc2626; }
.error { color: #dc2626; font-size: 14px; min-height: 18px; margin: 4px 0 0; }
button { margin-top: 12px; padding: 10px 16px; font-size: 16px; cursor: pointer; }
#success { color: #15803d; font-weight: bold; }
</style>
</head>
<body>
<form id="contact" novalidate>
<label for="cname">Name</label>
<input id="cname" type="text">
<p class="error" id="cname-error"></p>
<label for="cemail">Email</label>
<input id="cemail" type="email">
<p class="error" id="cemail-error"></p>
<button type="submit">Send</button>
</form>
<p id="success"></p>
<script>
const form = document.getElementById("contact");
const nameInput = document.getElementById("cname");
const emailInput = document.getElementById("cemail");
function check(input, message, isValid) {
const error = document.getElementById(input.id + "-error");
input.classList.toggle("invalid", !isValid);
error.textContent = isValid ? "" : message;
return isValid;
}
function checkName() {
return check(nameInput, "Please enter at least 2 characters.", nameInput.value.trim().length >= 2);
}
function checkEmail() {
return check(emailInput, "Please enter a valid email.", /^\S+@\S+\.\S+$/.test(emailInput.value));
}
nameInput.addEventListener("blur", checkName); // when leaving the field
emailInput.addEventListener("blur", checkEmail);
form.addEventListener("submit", function (event) {
event.preventDefault();
const nameOk = checkName();
const emailOk = checkEmail();
document.getElementById("success").textContent =
nameOk && emailOk ? "Thank you! We will contact you soon." : "";
});
</script>
</body>
</html>
Here checkName and checkEmail are passed by name (without parentheses) as handlers, and are also called by hand inside submit. The email pattern is a regular expression, which you studied in the regex lesson.
4. Click a button, show a message
The most common beginner feature, short and useful:
const likeButton = document.getElementById("like");
let likes = 0;
likeButton.addEventListener("click", function () {
likes++;
likeButton.textContent = "👍 " + likes;
});
5. Tooltip that follows the mouse
const tip = document.getElementById("tip"); // a small absolutely positioned <div>
const area = document.getElementById("area");
area.addEventListener("mousemove", function (event) {
tip.style.left = event.clientX + 12 + "px";
tip.style.top = event.clientY + 12 + "px";
});
area.addEventListener("mouseenter", function () { tip.hidden = false; });
area.addEventListener("mouseleave", function () { tip.hidden = true; });
Common mistakes
- Calling the function instead of passing it.
addEventListener("click", sayHi())runssayHiat once. WritesayHi, or wrap it:() => sayHi(). - Using
onclicktwice. The second one replaces the first. UseaddEventListener. - Running the script before the element exists.
getElementByIdreturnsnull, andnull.addEventListenercrashes. Put the script at the end of<body>, or usedefer(see the DOM introduction). - Mistyping the event name.
"onclick"is wrong foraddEventListener. It is"click", with noon. Event names are lowercase. - Listening for
clickon a submit button instead ofsubmiton the form. The Enter key then skips your code. - Forgetting
preventDefault()on a form. The page reloads and your result disappears. - Expecting
changeon every key. A text box fireschangeonly when you leave it. Useinput. - Using
keypress. It is deprecated. Usekeydown. - Adding listeners inside a loop or another handler again and again. The same handler then runs many times. Add it once.
- Doing heavy work in
mousemove,scrollorresize. They fire hundreds of times. Keep them light. - A
<button>in a form that should not submit. Addtype="button".
Practice
- Make a button that counts clicks. Add a second button that resets the counter.
- Show what the user types live in a paragraph below an input (use the
inputevent). - Change the text of the box on
dblclick, and make it highlighted onmouseenterand normal onmouseleave. - Show the
event.keyof every key pressed on the page. What do you see when you press space, Enter, and the arrow keys? - Add a
changelistener to a<select>and print the new value. Then add a checkbox and print whether it is checked. - Make the form show
"Welcome, <email>"on submit without reloading the page. Test it by pressing Enter inside the email field instead of clicking the button. - Compare
inputandchangeon the same text box by logging both. When does each one fire? - Build the live search filter, and add a counter that says how many items are visible (for example,
3 of 6 shown). - Make a character counter for a
<textarea>that stops showing red once the user is under the limit again. - Challenge: build a small “guess the key” game. Choose a random letter. When the user presses a key, tell them if it matches, and show how many tries they took.
Recap
- An event is a signal that something happened. Event-driven code waits for events and then runs a handler function.
- The best way to listen is
element.addEventListener("click", handler). Event names have noonprefix. Avoid inlineonclick="", and note thatel.onclick = ...allows only one handler. - Pass the function itself (
handler), not a call (handler()). - Mouse:
click,dblclick,contextmenu,mousedown,mouseup,mouseenter,mouseleave,mousemove. Prefermouseenter/mouseleavefor hover. - Keyboard:
keydownandkeyup. Useevent.keyto know which key. Do not usekeypress. - Form:
input(live),change(finished),focus,blur,submitandreset. Listen tosubmiton the form and callpreventDefault()to stop the reload. - Page:
DOMContentLoaded,load,resizeandscroll. Keep the busy ones (mousemove,scroll,resize) light. - Events power real features: counters, live search, shortcuts, validation and tooltips.
- Next you will learn the event object: the information the browser gives you about every event, such as
event.target.