HTML Element .addEventListener()
| Since: | DOM Level 2(2000) |
|---|
Registers an event listener on an HTML element and sets the function to be called when the specified event occurs.
Syntax
element.addEventListener("eventName", function);
// With options
element.addEventListener("eventName", function, options);
Arguments
| Argument | Description |
|---|---|
| eventName | Specifies the name of the event to listen for as a string. Common values include click, input, submit, keydown, scroll, and load. Do not include the on prefix. |
| function | Specifies the callback function to run when the event fires. The event object is passed as an argument. |
| options | Optional. Passing { once: true } causes the listener to fire only once and then be automatically removed. |
Common Events
| Event | Description |
|---|---|
| click | Fires when an HTML element is clicked. |
| input | Fires whenever the value of a text field changes. Triggers immediately on every keystroke. |
| change | Fires when a form element's value is committed. For text fields, this fires when the element loses focus. |
| submit | Fires when a form is submitted. |
| keydown | Fires when a key is pressed down. |
| keyup | Fires when a key is released. |
| mouseover | Fires when the mouse pointer moves over an HTML element. |
| mouseout | Fires when the mouse pointer leaves an HTML element. |
| scroll | Fires when the page or element is scrolled. |
| load | Fires when a page or image has finished loading. |
Sample Code
sample_addEventListener.html
<button id="btn">Send D-Mail</button> <button id="once-btn">Send once only</button> <input id="message" type="text" placeholder="Enter your message"> <p id="output"></p> <form id="lab-form"> <input type="text" id="member-name" placeholder="Lab member name"> <button type="submit">Register</button> </form>
sample_addEventListener.js
Pattern 1: Basic click event
var btn = document.getElementById("btn");
if (btn) {
btn.addEventListener("click", function() {
alert("D-Mail sent!");
});
}
Pattern 2: Real-time preview with the input event
var messageInput = document.getElementById("message");
var output = document.getElementById("output");
if (messageInput && output) {
messageInput.addEventListener("input", function(e) {
output.textContent = "Message: " + e.target.value;
});
}
Pattern 3: { once: true } — fires only once, then removes itself
var onceBtn = document.getElementById("once-btn");
if (onceBtn) {
onceBtn.addEventListener("click", function() {
output.textContent = "This will only appear once";
onceBtn.textContent = "Sent (no longer clickable)";
onceBtn.disabled = true;
}, { once: true });
}
Pattern 4: Prevent the default form submission with the submit event
var form = document.getElementById("lab-form");
if (form) {
form.addEventListener("submit", function(e) {
e.preventDefault(); // Prevent page navigation.
var name = document.getElementById("member-name").value;
if (!name) {
output.textContent = "Please enter a name.";
return;
}
output.textContent = "Lab member registered: " + name;
});
}
Pattern 5: Register multiple listeners on the same element
// btn already has the Pattern 1 listener registered.
if (btn) {
btn.addEventListener("click", function() {
output.textContent = "Click log: the click event fired";
});
// Both listeners run when the button is clicked.
}
Event Object Properties
An event object is passed as an argument to the callback function. The parameter name can be anything you choose, but e, evt, and event are commonly used by convention. Use these properties to access detailed information about the event.
| Property | Description |
|---|---|
| e.target | Returns the element where the event actually occurred. If a child element was clicked, that child is returned. |
| e.currentTarget | Returns the element the listener is registered on. The distinction from target is crucial for event delegation. |
| e.type | Returns the event type as a string ("click", "input", "submit", etc.). |
| e.preventDefault() | Prevents the browser's default action (form submission, link navigation, etc.). |
| e.stopPropagation() | Stops the event from propagating (bubbling) to parent elements. |
| e.key | For keyboard events, returns the value of the key pressed ("Enter", "Escape", "a", etc.). |
| e.clientX / e.clientY | For mouse events, returns the click coordinates (relative to the viewport). |
// Difference between target and currentTarget
var list = document.getElementById("member-list");
var output = document.getElementById("output");
if (list && output) {
list.addEventListener("click", function(e) {
output.textContent = "target: " + e.target.tagName + " (" + e.target.textContent + ") / currentTarget: " + e.currentTarget.tagName + " / type: " + e.type;
});
}
Event Delegation
Event delegation is a technique where you register a single listener on a parent element and use e.target to identify which child was clicked. This is crucial in practice because it automatically works for dynamically added elements.
sample_delegation.html
<ul id="member-list"> <li>Okabe Rintaro</li> <li>Makise Kurisu</li> <li>Shiina Mayuri</li> </ul> <button id="add-member">Add Lab Member</button> <p id="selected"></p>
sample_delegation.js
// Register a single listener on the parent (ul).
var memberList = document.getElementById("member-list");
var selected = document.getElementById("selected");
if (memberList && selected) {
memberList.addEventListener("click", function(e) {
// Check whether the clicked element is an li.
if (e.target.tagName === "LI") {
selected.textContent = "Selected: " + e.target.textContent;
}
});
}
// The listener above automatically works for dynamically added elements.
var addBtn = document.getElementById("add-member");
if (addBtn && memberList) {
var count = 4;
addBtn.addEventListener("click", function() {
var li = document.createElement("li");
li.textContent = "Lab Member No." + count;
memberList.appendChild(li);
count++;
});
}
Clicking dynamically added items like "Lab Member No.4" still triggers the parent's listener. Since you don't need to register listeners on individual li elements, this approach is also better for performance.
Removing Listeners (removeEventListener)
Use removeEventListener() to remove a registered listener. However, you need the same function reference that was used when registering. Listeners registered with anonymous functions cannot be removed.
<button id="btn">NG button (cannot be removed)</button> <button id="once-btn">OK button (can be removed)</button> <p id="output"></p>
// NG: Anonymous functions cannot be removed.
var btn = document.getElementById("btn");
var output = document.getElementById("output");
if (btn && output) {
btn.addEventListener("click", function() {
output.textContent = "Click (this function cannot be removed)";
});
btn.removeEventListener("click", function() {
output.textContent = "Click (this function cannot be removed)";
});
// Even though the code is identical, they are different function objects.
}
// OK: Named functions can be removed.
function handleClick() {
output.textContent = "Click (this function can be removed)";
}
var btn2 = document.getElementById("once-btn");
if (btn2 && output) {
btn2.addEventListener("click", handleClick);
// To remove later:
// btn2.removeEventListener("click", handleClick);
}
When you need to remove a listener (stopping scroll monitoring, auto-removing after a set number of executions, etc.), always use named functions. For one-time execution, the { once: true } option is more concise.
Keyboard Events
<input id="message" type="text" placeholder="Type and press Enter to send"> <p id="output"></p>
// Execute on Enter key press (form submission without a button)
var searchInput = document.getElementById("message");
var output = document.getElementById("output");
if (searchInput && output) {
searchInput.addEventListener("keydown", function(e) {
if (e.key === "Enter") {
output.textContent = "Sent: " + e.target.value;
}
});
}
// Clear input with the Escape key
document.addEventListener("keydown", function(e) {
if (e.key === "Escape") {
var input = document.getElementById("message");
if (input) {
input.value = "";
output.textContent = "Input cleared";
}
}
});
Use e.key for keyboard events. e.keyCode is deprecated, and e.key is widely used instead.
Difference from onclick
There are multiple ways to register events. addEventListener() is widely used in practice. It supports multiple listeners, removal, and options.
| Method | Multiple Listeners | Removal | Options |
|---|---|---|---|
| addEventListener() | Yes — multiple listeners can be registered | Yes — removeEventListener() | Yes — once, capture, passive, etc. |
| onclick = function() | No — reassignment overwrites the previous one | Limited — assign null | No |
| HTML attribute onclick="" | No — only one | Limited — removeAttribute() | No |
<button id="btn-onclick">onclick (overwrites)</button> <button id="btn-listener">addEventListener (both run)</button> <p id="output"></p>
var output = document.getElementById("output");
// With onclick, a later assignment overwrites the first.
var btnOnclick = document.getElementById("btn-onclick");
if (btnOnclick && output) {
btnOnclick.onclick = function() { output.textContent = "onclick: First"; };
btnOnclick.onclick = function() { output.textContent = "onclick: Second (first was overwritten)"; };
}
// With addEventListener, both run.
var btnListener = document.getElementById("btn-listener");
if (btnListener && output) {
btnListener.addEventListener("click", function() { output.textContent = "addEventListener: First → "; });
btnListener.addEventListener("click", function() { output.textContent += "Second (both executed)"; });
}
Common mistake 1: accessing DOM before it is built
Placing a script tag in the <head> causes JavaScript to run before the browser has finished parsing the HTML. At that point, DOM elements do not yet exist, so getElementById() returns null and the subsequent addEventListener() call throws an error.
<!-- NG: A script in <head> runs before the DOM is built --> <head> <script src="app.js"></script> </head> <body> <button id="btn">Send</button> </body>
// NG: btn is null here — this throws an error.
var btn = document.getElementById("btn");
btn.addEventListener("click", function() { // TypeError: Cannot read properties of null
alert("Sent!");
});
<!-- OK: Place the script tag at the end of <body> --> <body> <button id="btn">Send</button> <script src="app.js"></script> </body>
// OK: At the end of <body>, the DOM is fully built, so the element is found reliably.
var btn = document.getElementById("btn");
if (btn) {
btn.addEventListener("click", function() {
alert("Sent!");
});
}
If you must place the script in <head>, use the DOMContentLoaded event to work around the same problem.
// Using DOMContentLoaded (when the script is in <head>)
document.addEventListener("DOMContentLoaded", function() {
var btn = document.getElementById("btn");
if (btn) {
btn.addEventListener("click", function() {
alert("Sent!");
});
}
});
Common mistake 2: anonymous function in removeEventListener
removeEventListener() requires the exact same function reference that was passed to addEventListener(). Even if the code is identical, passing an anonymous function creates a new function object each time, so the listener is never actually removed.
// NG: Passing an anonymous function to remove() has no effect.
var btn = document.getElementById("btn");
var output = document.getElementById("output");
if (btn && output) {
btn.addEventListener("click", function() {
output.textContent = "Clicked";
});
// Even identical code creates a different object — removal does not work.
btn.removeEventListener("click", function() {
output.textContent = "Clicked";
});
}
// OK: Pass the same named-function reference.
function handleClick() {
output.textContent = "Clicked";
}
var btn = document.getElementById("btn");
var output = document.getElementById("output");
if (btn && output) {
btn.addEventListener("click", handleClick);
// Passing the same reference removes the listener reliably.
btn.removeEventListener("click", handleClick);
}
Common mistake 3: arrow function and this
Inside a regular function callback, this refers to the element that triggered the event (the same as e.currentTarget). Inside an arrow function, this inherits from the surrounding scope and does not refer to the element.
// NG: this inside an arrow function does not refer to the element.
var btn = document.getElementById("btn");
var output = document.getElementById("output");
if (btn && output) {
btn.addEventListener("click", () => {
output.textContent = "this: " + this; // window or undefined (strict mode)
this.style.color = "red"; // May throw an error.
});
}
// OK: Using the function keyword makes this refer to the clicked element.
var btn = document.getElementById("btn");
var output = document.getElementById("output");
if (btn && output) {
btn.addEventListener("click", function() {
output.textContent = "this.id: " + this.id; // Displays "btn".
this.style.color = "red"; // The button text turns red.
});
}
You can also access the element through e.currentTarget without relying on this. Combining an arrow function with e.currentTarget avoids this-related confusion entirely.
// OK: e.currentTarget works regardless of whether you use an arrow function.
var btn = document.getElementById("btn");
var output = document.getElementById("output");
if (btn && output) {
btn.addEventListener("click", function(e) {
output.textContent = "e.currentTarget.id: " + e.currentTarget.id;
e.currentTarget.style.color = "red";
});
}
Overview
element.addEventListener() is the fundamental method for event-driven programming. It lets you listen for user interactions (clicks, input, scrolling, etc.) and browser events (such as page load) and run any code when they occur.
The callback function receives an event object as its argument. This object contains useful properties and methods such as target (the element that triggered the event) and preventDefault() (which prevents the browser's default behavior).
You can attach multiple listeners to the same element for the same event. element.onclick = function() {} only allows a single listener, so element.addEventListener() is widely used in practice.
To remove a registered listener, use removeEventListener(). Note that the exact same function reference used when registering the listener is required, so listeners registered with anonymous functions cannot be removed. Use a named function if you need to remove the listener later.
element.addEventListener() is covered in more detail in the tutorial articles 'Event Handling (1)' and 'Event Handling (2)'.
Browser Compatibility
5 or earlier ×
6 or earlier ×
Android Browser
37+ ○
Chrome Android
36+ ○
17 or earlier ×
Firefox Android
79+ ○
3 or earlier ×If you find any errors or copyright issues, please contact us.