4.5 Module 4 · Document Object Model (DOM)

Event Listeners

Master addEventListener — the modern, flexible way to attach event handlers — and go beyond built-in events with custom events you create and dispatch yourself.

1

Definition

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.

2

Simple Way

Instead of writing events inside HTML, addEventListener lets you listen and react entirely in JavaScript:

element The DOM element you want to listen on — button, input, document…
"event" The event type as a string — "click", "keyup", "submit"
handler The function that runs when the event fires — receives the event object
remove removeEventListener() detaches the handler when it is no longer needed
3

Code Examples

Example 1 — Basic addEventListener()
<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>
Live Preview
Explanation
  • Syntax: element.addEventListener("eventType", handlerFunction)
  • The HTML is clean — no onclick="" in the markup at all
  • The handler function receives the event object automatically — add a parameter to use it
Example 2 — Multiple Listeners on One Element
<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>
Live Preview
Explanation
  • One element can have as many listeners as you need — impossible cleanly with inline onclick
  • Each listener is independent — adding a new one never overwrites an existing one
  • mouseover fires when the cursor enters; mouseout fires when it leaves
Example 3 — Named Function & removeEventListener()
<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>
Live Preview
Explanation
  • To remove a listener, pass the same named function reference — anonymous functions cannot be removed
  • The built-in { once: true } option does this automatically: addEventListener("click", fn, { once: true })
  • Always remove listeners you no longer need to avoid memory leaks
Example 4 — Custom Events
// 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
Output
Click ▶ Run to execute
Explanation
  • new CustomEvent(name, { detail: data }) — creates an event with any payload you want
  • element.dispatchEvent(event) — fires the event, triggering all matching listeners
  • Custom events let different modules of your application communicate without tight coupling
4

Real-Life Example

Think of a security guard assigned to a building entrance:

element
The door — the thing being watched and listened to
addEventListener
Assigning a guard to the door — "start watching for events"
Multiple listeners
Multiple guards assigned to the same door — each reacts differently
removeEventListener
Reassigning the guard elsewhere — "stop watching this door"
Custom event
A walkie-talkie message — one part of the system signals another
dispatchEvent
Pressing the send button on the walkie-talkie — triggering the custom signal
5

HTML + JavaScript Example

Click ▶ Preview to see named functions, multiple listeners, and a remove-on-first-click pattern all working together in one clean page.

Live in Browser — Professional Event Handling
<!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>
Live Preview
6

Tasks (Practice)

Easy Task 1 — Alert on click via addEventListener
Starter code
<button id="myBtn">Click Me</button>

<script>
  let btn = document.getElementById("myBtn");
  // attach listener here
</script>
  • Use addEventListener("click", ...) to show an alert on click
  • Move the handler into a named function — not an anonymous one
  • After the alert, use removeEventListener so the button only works once
Medium Task 2 — Multi-event hover + click tracker
Starter code
<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>
  • Add a click listener that increments a counter and updates status
  • Add mouseover → change button background to yellow
  • Add mouseout → reset button background to its original colour
7

MCQs

Q1
Which method is the modern standard for attaching event handlers?
  • A onclick=""
  • B addEvent()
  • C addEventListener()
  • D attachHandler()
💡 C is correct. addEventListener() 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().
Q2
Can you attach multiple listeners of the same event type to one element?
let btn = document.querySelector("button");

btn.addEventListener("click", () => console.log("First"));
btn.addEventListener("click", () => console.log("Second"));
Output
Run it — then click the button!
  • A No — the second replaces the first
  • B Yes — both handlers run in order
  • C Only the last one runs
  • D It throws an error
💡 B is correct. 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.
Q3
What is wrong with this code?
function showAlert() {
  console.log("Clicked!");
}

// Which line is correct?
// A:
document.querySelector("button").addEventListener("click", showAlert());
// B:
document.querySelector("button").addEventListener("click", showAlert);
Output
Run it to see the difference
  • A Line A is correct — parentheses are needed
  • B Line B is correct — no parentheses when passing the function
  • C Both are correct
  • D Neither works
💡 B is correct. 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.
8

Pro Tips & Extra Knowledge

  • 01

    Always pass a function reference to addEventListener — never call it with parentheses:

    Function reference vs function call
    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());
    Output
    Click ▶ Run to execute
  • 02

    Use the { once: true } option as a clean shortcut for one-time listeners — the browser removes it automatically after the first fire:

    { once: true } option
    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
    Output
    Click ▶ Run — then click the button
  • 03

    Use event delegation — attach one listener to a parent element instead of many listeners on individual children. This works for dynamically added elements too:

    Event delegation on a parent
    // 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
    Output
    Click ▶ Run — then click a list item
  • 04

    Use CustomEvent with a detail payload to pass data between loosely coupled parts of your application:

    CustomEvent with detail data
    // 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);
    Output
    Click ▶ Run to execute
Mini Challenge

Two listeners are attached to the same button for the same event — predict exactly what prints when you click, then verify.

What gets printed when the button is clicked?
let btn = document.querySelector("button");

btn.addEventListener("click", () => {
  console.log("First");
});

btn.addEventListener("click", () => {
  console.log("Second");
});
Output
Think first, then run!
Step-by-Step Explanation
// 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)