Master addEventListener — the modern, flexible way to attach
event handlers — and go beyond built-in events with custom events you create
and dispatch yourself.
An Event Listener is a method in JavaScript —
addEventListener() — that attaches an event handler to an
element, allowing it to listen for a specific event and execute a function
when that event occurs.
Unlike inline event attributes (onclick="..."), event listeners
keep your HTML and JavaScript completely separate, allow multiple handlers on
the same element, and can be removed when no longer needed.
Instead of writing events inside HTML, addEventListener
lets you listen and react entirely in JavaScript:
"click", "keyup", "submit"…
event object
removeEventListener() detaches the handler when it is no longer needed
<button id="btn">Click Me</button>
<p id="msg"></p>
<script>
let btn = document.getElementById("btn");
btn.addEventListener("click", function() {
document.getElementById("msg").textContent = "Button was clicked!";
});
</script>
element.addEventListener("eventType", handlerFunction)onclick="" in the markup at allevent object automatically — add a parameter to use it<button id="btn2">Hover or Click Me</button>
<p id="log"></p>
<script>
let btn = document.getElementById("btn2");
let log = document.getElementById("log");
btn.addEventListener("click", () => {
log.textContent = "Clicked!";
log.style.color = "#e07b00";
});
btn.addEventListener("mouseover", () => {
log.textContent = "Hovering...";
log.style.color = "#3b82f6";
});
btn.addEventListener("mouseout", () => {
log.textContent = "Mouse left.";
log.style.color = "#64748b";
});
</script>
onclickmouseover fires when the cursor enters; mouseout fires when it leaves<button id="once">Click once only</button>
<p id="status">Waiting...</p>
<script>
let btn = document.getElementById("once");
let status = document.getElementById("status");
function handleOnce() {
status.textContent = "Clicked! Listener removed — won't fire again.";
btn.removeEventListener("click", handleOnce); // detach after first click
}
btn.addEventListener("click", handleOnce);
</script>
{ once: true } option does this automatically: addEventListener("click", fn, { once: true })// Create a custom event named "greet"
let greetEvent = new CustomEvent("greet", {
detail: { name: "Aman" } // pass custom data
});
// Listen for it
document.addEventListener("greet", function(e) {
console.log("Hello,", e.detail.name + "!");
});
// Fire it manually
document.dispatchEvent(greetEvent);
// Custom events let different parts of your code communicate
// without direct function calls — a powerful decoupling pattern
new CustomEvent(name, { detail: data }) — creates an event with any payload you wantelement.dispatchEvent(event) — fires the event, triggering all matching listenersThink of a security guard assigned to a building entrance:
Click ▶ Preview to see named functions, multiple listeners, and a remove-on-first-click pattern all working together in one clean page.
<!DOCTYPE html>
<html>
<head><title>Event Listeners</title></head>
<body style="font-family:sans-serif;padding:24px;background:#f8f9fa">
<button id="mainBtn"
style="padding:10px 22px;background:#f7df1e;border:none;border-radius:8px;
cursor:pointer;font-weight:700;font-size:1rem">
Click Me
</button>
<button id="onceBtn"
style="margin-left:10px;padding:10px 22px;background:#82aaff;border:none;
border-radius:8px;cursor:pointer;font-weight:700;font-size:1rem;color:#111">
Click Once Only
</button>
<div id="log"
style="margin-top:16px;font-family:monospace;font-size:0.9rem;background:#1e1e2e;
color:#cdd6f4;border-radius:8px;padding:14px;min-height:60px;line-height:2">
Waiting for events...
</div>
<script>
let log = document.getElementById("log");
let count = 0;
function addLog(msg) {
log.innerHTML = msg + "<br>" + log.innerHTML;
}
// Named functions — reusable and removable
function onClick() { addLog("🖱 Click #" + (++count)); }
function onMouseOver() { addLog("👀 Mouse entered button"); }
function onMouseOut() { addLog("👋 Mouse left button"); }
let mainBtn = document.getElementById("mainBtn");
mainBtn.addEventListener("click", onClick);
mainBtn.addEventListener("mouseover", onMouseOver);
mainBtn.addEventListener("mouseout", onMouseOut);
// One-shot listener
let onceBtn = document.getElementById("onceBtn");
function handleOnce() {
addLog("⚡ Fired once — listener removed");
onceBtn.textContent = "Already clicked";
onceBtn.style.opacity = "0.5";
onceBtn.removeEventListener("click", handleOnce);
}
onceBtn.addEventListener("click", handleOnce);
</script>
</body>
</html>
<button id="myBtn">Click Me</button>
<script>
let btn = document.getElementById("myBtn");
// attach listener here
</script>
addEventListener("click", ...) to show an alert on clickremoveEventListener so the button only works once<button id="trackBtn">Interact with me</button>
<p id="status"></p>
<script>
let btn = document.getElementById("trackBtn");
let status = document.getElementById("status");
// add click, mouseover, mouseout listeners
</script>
click listener that increments a counter and updates statusmouseover → change button background to yellowmouseout → reset button background to its original colouraddEventListener() is the standard DOM method for attaching event handlers. It separates JavaScript from HTML, allows multiple handlers on one element, and supports removal via removeEventListener().
let btn = document.querySelector("button");
btn.addEventListener("click", () => console.log("First"));
btn.addEventListener("click", () => console.log("Second"));
addEventListener() adds a listener to the element's list — it never removes existing ones. Both handlers run when the button is clicked, in the order they were added.
function showAlert() {
console.log("Clicked!");
}
// Which line is correct?
// A:
document.querySelector("button").addEventListener("click", showAlert());
// B:
document.querySelector("button").addEventListener("click", showAlert);
showAlert() with parentheses calls the function immediately and passes its return value (undefined) as the handler. showAlert without parentheses passes the function reference — which is what you want.
Always pass a function reference to addEventListener — never call it with parentheses:
function greet() { console.log("Hello!"); }
// ✗ Wrong — executes immediately, passes undefined as handler
document.querySelector("button").addEventListener("click", greet());
// ✓ Correct — passes the function itself as the handler
document.querySelector("button").addEventListener("click", greet);
// ✓ Also correct — arrow function is already a reference
document.querySelector("button").addEventListener("click", () => greet());
Use the { once: true } option as a clean shortcut for one-time listeners — the browser removes it automatically after the first fire:
let btn = document.querySelector("button");
// Fires only once — auto-removed after first click
btn.addEventListener("click", function handler() {
console.log("This fires only once!");
}, { once: true });
// Equivalent to calling removeEventListener inside the handler
// but shorter and cleaner
Use event delegation — attach one listener to a parent element instead of many listeners on individual children. This works for dynamically added elements too:
// One listener on the parent handles clicks on ALL children
document.querySelector("ul").addEventListener("click", function(e) {
if (e.target.tagName === "LI") {
console.log("You clicked:", e.target.textContent);
}
});
// Works even for <li> elements added AFTER this listener is set up
Use CustomEvent with a detail payload to pass data between loosely coupled parts of your application:
// Dispatch a custom event with payload
let event = new CustomEvent("userLogin", {
detail: { username: "Aman", role: "admin" }
});
// Any module can listen for it
document.addEventListener("userLogin", function(e) {
console.log("User logged in:", e.detail.username);
console.log("Role:", e.detail.role);
});
// Trigger it from anywhere
document.dispatchEvent(event);
Two listeners are attached to the same button for the same event — predict exactly what prints when you click, then verify.
let btn = document.querySelector("button");
btn.addEventListener("click", () => {
console.log("First");
});
btn.addEventListener("click", () => {
console.log("Second");
});
// addEventListener ADDS to the element's listener list
// It never overwrites or replaces an existing listener
// After the two calls, the button has TWO click listeners:
// 1. () => console.log("First")
// 2. () => console.log("Second")
// When the button is clicked, both run in the ORDER they were added:
// Output:
// First
// Second
// Compare to onclick (inline or property):
// btn.onclick = () => console.log("First");
// btn.onclick = () => console.log("Second");
// ← Only "Second" would run — the second assignment overwrites the first!
// Key takeaway:
// addEventListener → ADDS (both run)
// onclick = ... → REPLACES (only last one runs)