LearnContact
Lesson 2270 min read

JavaScript Events

Learn how JavaScript responds to user actions and browser activity using events. Master event listeners, event objects, mouse events, keyboard events, form events, event propagation, delegation, default behavior, and real-world interactive interfaces.

Introduction

The DOM allows JavaScript to find and modify HTML elements. However, most modern web applications must do more than simply change a page when the script first runs. They need to respond when users click buttons, type into inputs, submit forms, press keyboard keys, move the mouse, scroll the page, resize the browser, or interact with other parts of the interface.

JavaScript events make this possible. An event is a signal that something has happened in the browser. JavaScript can listen for that signal and execute code in response.

Events are the foundation of interactive web development. Buttons, menus, forms, modals, tabs, games, search boxes, shopping carts, dashboards, drag-and-drop interfaces, and keyboard shortcuts all depend on events.

What You Will Learn
  • What JavaScript events are.
  • Why events are required for interactive applications.
  • How event-driven programming works.
  • The most common browser event types.
  • Different ways to handle events.
  • Why addEventListener() is the recommended approach.
  • How to create named, anonymous, and arrow function handlers.
  • How to attach multiple listeners.
  • How to remove event listeners.
  • What the event object contains.
  • How event.target works.
  • How event.currentTarget works.
  • The difference between target and currentTarget.
  • How mouse events work.
  • How to read mouse coordinates.
  • How keyboard events work.
  • The difference between key and code.
  • How modifier keys are detected.
  • How input and change events work.
  • How focus and blur events work.
  • How form submission events work.
  • How to read form data.
  • How FormData works.
  • How preventDefault() works.
  • What event propagation means.
  • How event capturing works.
  • How event bubbling works.
  • How stopPropagation() works.
  • How stopImmediatePropagation() works.
  • What event delegation is.
  • Why delegation is useful for dynamic elements.
  • How to use closest() in delegated handlers.
  • How listener options work.
  • How once, capture, passive, and signal options work.
  • How page, document, and window events work.
  • How clipboard events work.
  • How drag-and-drop events work.
  • How custom events are created and dispatched.
  • How this behaves inside event handlers.
  • How to build a complete interactive application.

What is an Event?

An event is a notification that something has happened in the browser. The action may come from the user, the browser, the document, or JavaScript itself.

Event Examples
USER ACTION

Click Button
      │
      ▼
click Event


Type in Input
      │
      ▼
input Event


Press Keyboard Key
      │
      ▼
keydown Event


Submit Form
      │
      ▼
submit Event


Move Mouse
      │
      ▼
mousemove Event


BROWSER ACTION

Page Finished Loading
      │
      ▼
load Event


Window Resized
      │
      ▼
resize Event


Page Scrolled
      │
      ▼
scroll Event

JavaScript can listen for these events and execute a function when a specific event occurs.

Why Do We Need Events?

Without events, JavaScript code would run according to the normal execution flow and would not know when a user interacts with the page. Events allow applications to wait for actions and respond at the correct time.

Button Actions

Run code when users click buttons, links, icons, or cards.

Keyboard Input

Respond to typing, shortcuts, and specific keyboard keys.

Form Handling

Validate fields and process form submissions.

Live Search

Filter results while users type.

Navigation

Open menus, switch tabs, and control mobile navigation.

Modals

Open and close dialog windows.

Games

Respond to keyboard, mouse, and touch controls.

Shopping Carts

Add, remove, and update products based on user actions.

Dashboards

Respond to filters, selections, and interface controls.

Dynamic Interfaces

Update the page immediately without a full reload.

Real-World Analogy

Think of an event listener as a doorbell. The system waits until someone presses the button. When the button is pressed, the bell rings and a response happens.

Doorbell Analogy
DOORBELL SYSTEM

Person Presses Button
        │
        ▼
┌──────────────────────┐
│      DOORBELL        │
│                      │
│ Waiting for press... │
└──────────┬───────────┘
           │
           ▼
     BUTTON PRESSED
           │
           ▼
        EVENT
           │
           ▼
      BELL RINGS
           │
           ▼
       RESPONSE


JAVASCRIPT

User Clicks Button
        │
        ▼
     click Event
        │
        ▼
Event Listener Detects It
        │
        ▼
Handler Function Runs
        │
        ▼
Interface Updates

How Events Work

JavaScript event handling usually follows a simple process. First, select an element. Next, register a listener for a specific event. Finally, provide a function that should run when the event occurs.

Event Handling Process
1. SELECT ELEMENT

const button =
    document.querySelector(
        'button'
    );

        │
        ▼

2. LISTEN FOR EVENT

button.addEventListener(
    'click',
    ...
);

        │
        ▼

3. USER PERFORMS ACTION

User clicks button

        │
        ▼

4. EVENT OCCURS

click

        │
        ▼

5. HANDLER FUNCTION RUNS

function () {
    console.log('Clicked');
}

        │
        ▼

6. APPLICATION RESPONDS
index.html
<button id="welcomeButton">
    Click Me
</button>
script.js
const button =
    document.getElementById(
        'welcomeButton'
    );

button.addEventListener(
    'click',
    function () {
        console.log(
            'Button clicked!'
        );
    }
);
Console Output After Click

Event-Driven Programming

JavaScript applications are often event-driven. This means the program does not simply execute every interaction in a fixed sequence. Instead, parts of the program wait for events and respond when those events occur.

Traditional vs Event-Driven Flow
TRADITIONAL FLOW

Start
  │
  ▼
Step 1
  │
  ▼
Step 2
  │
  ▼
Step 3
  │
  ▼
End


EVENT-DRIVEN FLOW

Application Starts
        │
        ▼
┌────────────────────────────┐
│        WAITING             │
│                            │
│ Click?                     │
│ Key Press?                 │
│ Form Submit?               │
│ Input Change?              │
│ Scroll?                    │
└─────────────┬──────────────┘
              │
      ┌───────┼────────┐
      ▼       ▼        ▼
    Click   Keydown   Submit
      │       │        │
      ▼       ▼        ▼
   Handler  Handler  Handler

Common Event Types

Mouse Events

click, dblclick, mousedown, mouseup, mousemove, mouseenter, and mouseleave.

Keyboard Events

keydown and keyup.

Input Events

input and change.

Focus Events

focus, blur, focusin, and focusout.

Form Events

submit and reset.

Document Events

DOMContentLoaded and visibility-related events.

Window Events

load, resize, scroll, and beforeunload.

Clipboard Events

copy, cut, and paste.

Drag Events

dragstart, dragover, drop, and related events.

Custom Events

Application-specific events created by JavaScript.

Ways to Handle Events

JavaScript provides several ways to attach event handlers. Some approaches are older and less flexible, while addEventListener() is the recommended general-purpose approach.

Event Handling Methods
1. INLINE HTML

<button onclick="...">


2. EVENT PROPERTY

button.onclick = function () {
    ...
};


3. addEventListener()

button.addEventListener(
    'click',
    function () {
        ...
    }
);

Inline Event Handlers

An inline event handler places JavaScript directly inside an HTML event attribute.

Inline Click Handler
<button
    onclick="showMessage()"
>
    Click Me
</button>

<script>
    function showMessage() {
        console.log(
            'Button clicked'
        );
    }
</script>
Why Inline Handlers Are Usually Avoided
  • They mix HTML structure with JavaScript behavior.
  • They become difficult to maintain in large applications.
  • They encourage global functions.
  • They provide less flexibility than addEventListener().
  • They make separation of concerns weaker.

Event Properties

Another approach is assigning a function to an event property such as onclick.

onclick Property
const button =
    document.querySelector(
        'button'
    );

button.onclick =
    function () {
        console.log(
            'Button clicked'
        );
    };

The main limitation is that assigning another function to the same property replaces the previous handler.

Handler Replacement
button.onclick =
    function () {
        console.log('First');
    };


button.onclick =
    function () {
        console.log('Second');
    };
Output After Click

addEventListener()

The addEventListener() method registers a function that should run when a specified event occurs.

Basic Event Listener
const button =
    document.querySelector(
        'button'
    );

button.addEventListener(
    'click',
    function () {
        console.log(
            'Button clicked'
        );
    }
);
Method Structure
element.addEventListener(
    eventType,
    handlerFunction
);


element

The object that receives
the event.


eventType

The event name without "on".


handlerFunction

The function that runs
when the event occurs.

Why Use addEventListener()?

Multiple Listeners

Several handlers can listen for the same event.

Removable

Named listeners can be removed later.

Listener Options

Supports capture, once, passive, and signal options.

Separation

Keeps JavaScript behavior separate from HTML structure.

Propagation Control

Supports capturing and bubbling phases.

Scalable

Works well in small scripts and large applications.

Event Listener Syntax

General Syntax
element.addEventListener(
    'eventName',
    handler
);
Click Example
const button =
    document.getElementById(
        'saveButton'
    );

button.addEventListener(
    'click',
    function () {
        console.log(
            'Saved'
        );
    }
);
Do Not Add "on" to Event Names
  • Use click, not onclick.
  • Use submit, not onsubmit.
  • Use input, not oninput.
  • Use keydown, not onkeydown.

Named Event Handler Functions

A named function can be defined separately and passed to addEventListener(). This is useful when the handler is reusable or needs to be removed later.

Named Handler
const button =
    document.querySelector(
        'button'
    );


function handleClick() {
    console.log(
        'Button clicked'
    );
}


button.addEventListener(
    'click',
    handleClick
);
Pass the Function, Do Not Call It
  • Correct: addEventListener("click", handleClick)
  • Incorrect: addEventListener("click", handleClick())
  • The listener needs the function itself.
  • The browser calls the function when the event occurs.

Anonymous Event Handlers

Anonymous Handler
const button =
    document.querySelector(
        'button'
    );

button.addEventListener(
    'click',
    function () {
        console.log(
            'Clicked'
        );
    }
);

Anonymous handlers are convenient for small logic that does not need to be reused or removed separately.

Arrow Function Event Handlers

Arrow Function Handler
const button =
    document.querySelector(
        'button'
    );

button.addEventListener(
    'click',
    () => {
        console.log(
            'Clicked'
        );
    }
);

Arrow functions are concise, but they do not create their own this value. This difference matters when code expects this to refer to the element receiving the event.

Multiple Event Listeners

addEventListener() allows multiple handlers for the same event on the same element.

Multiple Click Listeners
const button =
    document.querySelector(
        'button'
    );


button.addEventListener(
    'click',
    function () {
        console.log(
            'First listener'
        );
    }
);


button.addEventListener(
    'click',
    function () {
        console.log(
            'Second listener'
        );
    }
);
Output After Click

removeEventListener()

The removeEventListener() method removes a previously registered event listener. The same function reference must be provided.

Remove Listener
const button =
    document.querySelector(
        'button'
    );


function handleClick() {
    console.log(
        'Clicked'
    );
}


button.addEventListener(
    'click',
    handleClick
);


button.removeEventListener(
    'click',
    handleClick
);
Incorrect Removal
button.addEventListener(
    'click',
    function () {
        console.log(
            'Clicked'
        );
    }
);


// ❌ Different function object
button.removeEventListener(
    'click',
    function () {
        console.log(
            'Clicked'
        );
    }
);
Function Identity Matters
  • Two functions with identical code are still different function objects.
  • Store the handler in a variable or use a named function when removal is required.
  • The event type must also match.

The Event Object

When an event occurs, the browser creates an event object containing information about what happened. The object is automatically passed to the handler function.

Access Event Object
const button =
    document.querySelector(
        'button'
    );

button.addEventListener(
    'click',
    function (event) {
        console.log(event);
    }
);

The parameter name can technically be anything, but event or e is commonly used.

Common Event Parameter Names
function (event) {
    console.log(event);
}


function (e) {
    console.log(e);
}

event.type

The type property contains the name of the event that occurred.

Read Event Type
button.addEventListener(
    'click',
    function (event) {
        console.log(
            event.type
        );
    }
);
Output

event.target

The target property refers to the element where the event originated.

index.html
<button id="saveButton">
    <span>Save</span>
</button>
Event Target
const button =
    document.getElementById(
        'saveButton'
    );

button.addEventListener(
    'click',
    function (event) {
        console.log(
            event.target
        );
    }
);

If the user clicks directly on the span inside the button, event.target may be the span because that is where the event started.

event.currentTarget

The currentTarget property refers to the element whose event listener is currently executing.

Current Target
button.addEventListener(
    'click',
    function (event) {
        console.log(
            event.currentTarget
        );
    }
);

In this example, currentTarget is always the button because the listener is attached to the button.

target vs currentTarget

index.html
<button id="saveButton">
    <span>
        Save
    </span>
</button>
Compare Targets
const button =
    document.getElementById(
        'saveButton'
    );

button.addEventListener(
    'click',
    function (event) {
        console.log(
            'Target:',
            event.target
        );

        console.log(
            'Current Target:',
            event.currentTarget
        );
    }
);
Target Comparison
User Clicks <span>

        │
        ▼

event.target

<span>

The element where
the event originated.


event.currentTarget

<button>

The element whose
listener is running.

Mouse Events

Mouse events occur when users interact with pointing devices such as a mouse or trackpad.

Common Mouse Events
click

dblclick

mousedown

mouseup

mousemove

mouseenter

mouseleave

mouseover

mouseout

click Event

The click event occurs when an element is activated with a pointing device. It is one of the most commonly used events.

Click Event
const button =
    document.querySelector(
        'button'
    );

button.addEventListener(
    'click',
    function () {
        console.log(
            'Button clicked'
        );
    }
);

dblclick Event

Double Click Event
const box =
    document.querySelector(
        '.box'
    );

box.addEventListener(
    'dblclick',
    function () {
        console.log(
            'Double clicked'
        );
    }
);

mousedown Event

The mousedown event occurs when a mouse button is pressed while the pointer is over an element.

Mouse Down
box.addEventListener(
    'mousedown',
    function () {
        console.log(
            'Mouse button pressed'
        );
    }
);

mouseup Event

The mouseup event occurs when a pressed mouse button is released.

Mouse Up
box.addEventListener(
    'mouseup',
    function () {
        console.log(
            'Mouse button released'
        );
    }
);

mousemove Event

The mousemove event occurs repeatedly while the pointer moves over an element.

Mouse Move
const area =
    document.querySelector(
        '.tracking-area'
    );

area.addEventListener(
    'mousemove',
    function (event) {
        console.log(
            event.clientX,
            event.clientY
        );
    }
);
High-Frequency Event
  • mousemove can fire many times per second.
  • Avoid expensive work inside the handler.
  • Performance techniques may be required for complex interfaces.

mouseenter Event

The mouseenter event occurs when the pointer enters an element.

Mouse Enter
const card =
    document.querySelector(
        '.card'
    );

card.addEventListener(
    'mouseenter',
    function () {
        card.classList.add(
            'highlighted'
        );
    }
);

mouseleave Event

Mouse Leave
card.addEventListener(
    'mouseleave',
    function () {
        card.classList.remove(
            'highlighted'
        );
    }
);

mouseover Event

The mouseover event occurs when the pointer enters an element or one of its descendants. Unlike mouseenter, it participates in event bubbling.

Mouse Over
card.addEventListener(
    'mouseover',
    function () {
        console.log(
            'Pointer entered'
        );
    }
);

mouseout Event

The mouseout event occurs when the pointer leaves an element or moves between its descendants. It participates in event bubbling.

Mouse Out
card.addEventListener(
    'mouseout',
    function () {
        console.log(
            'Pointer left'
        );
    }
);

Mouse Coordinates

Mouse event objects provide several coordinate properties that describe the pointer position.

Mouse Coordinates
document.addEventListener(
    'click',
    function (event) {
        console.log(
            'Client:',
            event.clientX,
            event.clientY
        );

        console.log(
            'Page:',
            event.pageX,
            event.pageY
        );

        console.log(
            'Screen:',
            event.screenX,
            event.screenY
        );
    }
);
Coordinate Types
clientX / clientY

Relative to browser viewport.


pageX / pageY

Relative to entire document.


screenX / screenY

Relative to physical screen.

Mouse Button Information

Detect Mouse Button
document.addEventListener(
    'mousedown',
    function (event) {
        console.log(
            event.button
        );
    }
);
Mouse Button Values
0 → Main Button

Usually left mouse button


1 → Auxiliary Button

Usually middle mouse button


2 → Secondary Button

Usually right mouse button

Keyboard Events

Keyboard events allow JavaScript to respond when users press or release keyboard keys.

Keyboard Event Flow
User Presses Key

      │
      ▼

keydown

      │
      ▼

Key Remains Pressed

      │
      ▼

keydown may repeat

      │
      ▼

User Releases Key

      │
      ▼

keyup

keydown Event

The keydown event occurs when a keyboard key is pressed. Depending on the key and operating system, it may repeat while the key remains held down.

Key Down
document.addEventListener(
    'keydown',
    function (event) {
        console.log(
            event.key
        );
    }
);

keyup Event

The keyup event occurs when a pressed keyboard key is released.

Key Up
document.addEventListener(
    'keyup',
    function (event) {
        console.log(
            event.key
        );
    }
);

event.key

The key property represents the meaning or character produced by the key press.

Read Key
document.addEventListener(
    'keydown',
    function (event) {
        console.log(
            event.key
        );
    }
);
Possible Output

event.code

The code property represents the physical key position on the keyboard.

Read Code
document.addEventListener(
    'keydown',
    function (event) {
        console.log(
            event.code
        );
    }
);
Possible Output

key vs code

Keyboard Property Comparison
event.key

Represents:
Meaning or produced value

Examples:

"a"
"A"
"Enter"
"Escape"


event.code

Represents:
Physical keyboard position

Examples:

"KeyA"
"Enter"
"Escape"
"Space"
When to Use Each
  • Use key when the meaning or typed character matters.
  • Use code when the physical key position matters.
  • Games often use code for movement controls.
  • Text shortcuts often use key.

Modifier Keys

Keyboard and mouse event objects provide properties for checking whether modifier keys are active.

Modifier Keys
document.addEventListener(
    'keydown',
    function (event) {
        console.log(
            'Ctrl:',
            event.ctrlKey
        );

        console.log(
            'Shift:',
            event.shiftKey
        );

        console.log(
            'Alt:',
            event.altKey
        );

        console.log(
            'Meta:',
            event.metaKey
        );
    }
);
Keyboard Shortcut
document.addEventListener(
    'keydown',
    function (event) {
        if (
            event.ctrlKey
            &&
            event.key === 's'
        ) {
            event.preventDefault();

            console.log(
                'Custom Save'
            );
        }
    }
);

Input Events

Input events are used to respond when users change form controls.

input Event

The input event occurs whenever the value of an input changes through user interaction. It is commonly used for live search, character counters, previews, and validation.

index.html
<input
    id="searchInput"
    type="text"
    placeholder="Search..."
>

<p id="preview"></p>
Live Input
const input =
    document.getElementById(
        'searchInput'
    );

const preview =
    document.getElementById(
        'preview'
    );

input.addEventListener(
    'input',
    function () {
        preview.textContent =
            input.value;
    }
);

change Event

The change event occurs when the value of a form control is committed. The exact timing depends on the type of control.

Select Change
const language =
    document.getElementById(
        'language'
    );

language.addEventListener(
    'change',
    function () {
        console.log(
            language.value
        );
    }
);

input vs change

Input Event Comparison
input Event

Runs:
As the value changes

Best For:
Live feedback
Live search
Character counters
Instant validation


change Event

Runs:
When a change is committed

Best For:
Select controls
Checkboxes
Radio buttons
Final value changes

Focus Events

Focus events occur when elements receive or lose keyboard focus. They are especially useful with forms and accessible interfaces.

focus Event

Focus Event
const input =
    document.querySelector(
        'input'
    );

input.addEventListener(
    'focus',
    function () {
        input.classList.add(
            'focused'
        );
    }
);

blur Event

Blur Event
input.addEventListener(
    'blur',
    function () {
        input.classList.remove(
            'focused'
        );
    }
);

focusin and focusout

The focusin and focusout events are similar to focus and blur, but they bubble. This makes them useful when a parent element needs to detect focus changes inside its descendants.

Detect Focus Inside Form
const form =
    document.querySelector(
        'form'
    );

form.addEventListener(
    'focusin',
    function (event) {
        console.log(
            'Focused:',
            event.target
        );
    }
);

Form Events

Forms have dedicated events for submission and resetting. JavaScript commonly listens for the submit event on the form itself.

submit Event

index.html
<form id="loginForm">
    <input
        id="email"
        type="email"
        required
    >

    <button type="submit">
        Login
    </button>
</form>
Handle Form Submission
const form =
    document.getElementById(
        'loginForm'
    );

form.addEventListener(
    'submit',
    function (event) {
        event.preventDefault();

        console.log(
            'Form submitted'
        );
    }
);
Listen on the Form
  • Users can submit forms in multiple ways.
  • They may click the submit button.
  • They may press Enter in a form field.
  • Listening for submit on the form handles the form submission itself.

Reading Form Data

index.html
<form id="profileForm">
    <input
        name="username"
        value="Aarav"
    >

    <input
        name="email"
        value="aarav@example.com"
    >

    <button type="submit">
        Save
    </button>
</form>
Read Form Controls
const form =
    document.getElementById(
        'profileForm'
    );

form.addEventListener(
    'submit',
    function (event) {
        event.preventDefault();

        console.log(
            form.elements.username.value
        );

        console.log(
            form.elements.email.value
        );
    }
);

FormData

The FormData object collects values from form controls that have name attributes.

Create FormData
const form =
    document.getElementById(
        'profileForm'
    );

form.addEventListener(
    'submit',
    function (event) {
        event.preventDefault();

        const formData =
            new FormData(form);

        console.log(
            formData.get(
                'username'
            )
        );

        console.log(
            formData.get(
                'email'
            )
        );
    }
);
Loop Through FormData
for (
    const [key, value]
    of formData.entries()
) {
    console.log(
        key,
        value
    );
}

Reset Event

Form Reset Event
const form =
    document.querySelector(
        'form'
    );

form.addEventListener(
    'reset',
    function () {
        console.log(
            'Form reset'
        );
    }
);

preventDefault()

The preventDefault() method prevents the browser from performing the default action associated with an event.

Prevent Form Submission
form.addEventListener(
    'submit',
    function (event) {
        event.preventDefault();

        console.log(
            'Custom form handling'
        );
    }
);
Prevent Link Navigation
const link =
    document.querySelector(
        'a'
    );

link.addEventListener(
    'click',
    function (event) {
        event.preventDefault();

        console.log(
            'Navigation prevented'
        );
    }
);

Default Browser Behavior

Default Actions
FORM SUBMIT

Default:
Send form and navigate/reload


LINK CLICK

Default:
Navigate to URL


CHECKBOX CLICK

Default:
Toggle checked state


CONTEXT MENU

Default:
Open browser context menu


DRAG

Default:
Browser-specific drag behavior
Do Not Prevent Defaults Without a Reason
  • Default browser behavior often provides useful accessibility.
  • Prevent only the specific behavior your application replaces.
  • Make sure custom behavior remains understandable and usable.

Checking Default Prevention

The defaultPrevented property indicates whether preventDefault() has been called for the event.

Check Default Prevention
element.addEventListener(
    'click',
    function (event) {
        event.preventDefault();

        console.log(
            event.defaultPrevented
        );
    }
);
Output

Event Propagation

When elements are nested, an event does not exist only on the deepest element. The browser processes the event through a propagation path containing ancestor elements.

Nested Elements
<div class="outer">
    <div class="middle">
        <button class="inner">
            Click Me
        </button>
    </div>
</div>
Propagation Phases
1. CAPTURING PHASE

window
  │
  ▼
document
  │
  ▼
html
  │
  ▼
body
  │
  ▼
outer
  │
  ▼
middle
  │
  ▼

2. TARGET PHASE

button

  │
  ▼

3. BUBBLING PHASE

middle
  │
  ▼
outer
  │
  ▼
body
  │
  ▼
html
  │
  ▼
document
  │
  ▼
window

Event Capturing

During the capturing phase, the event travels from the outermost ancestor toward the target element.

Capturing Direction
WINDOW

   │
   ▼

DOCUMENT

   │
   ▼

BODY

   │
   ▼

PARENT

   │
   ▼

TARGET

Target Phase

The target phase occurs when the event reaches the element where it originated.

Target Phase
User Clicks Button

        │
        ▼

┌─────────────────────┐
│       BUTTON        │
│                     │
│    EVENT TARGET     │
└─────────────────────┘

Event Bubbling

During the bubbling phase, the event travels from the target upward through its ancestors. Most commonly used event listeners run during this phase by default.

Bubbling Direction
TARGET

   │
   ▼

PARENT

   │
   ▼

BODY

   │
   ▼

DOCUMENT

   │
   ▼

WINDOW

Propagation Flow

Complete Event Propagation
                 WINDOW
                    │
                    ▼
                DOCUMENT
                    │
                    ▼
                  HTML
                    │
                    ▼
                  BODY
                    │
                    ▼
                 PARENT
                    │
                    ▼
                  BUTTON
                    │
                    │
              EVENT TARGET
                    │
                    ▼
                 PARENT
                    │
                    ▼
                  BODY
                    │
                    ▼
                  HTML
                    │
                    ▼
                DOCUMENT
                    │
                    ▼
                 WINDOW


TOP TO TARGET

Capturing Phase


AT TARGET

Target Phase


TARGET TO TOP

Bubbling Phase

Bubbling Example

index.html
<div id="parent">
    <button id="child">
        Click Me
    </button>
</div>
Bubbling Listeners
const parent =
    document.getElementById(
        'parent'
    );

const child =
    document.getElementById(
        'child'
    );


parent.addEventListener(
    'click',
    function () {
        console.log(
            'Parent'
        );
    }
);


child.addEventListener(
    'click',
    function () {
        console.log(
            'Child'
        );
    }
);
Output After Clicking Button

Capturing Example

Pass true or use the capture option to run a listener during the capturing phase.

Capturing Listener
parent.addEventListener(
    'click',
    function () {
        console.log(
            'Parent'
        );
    },
    true
);


child.addEventListener(
    'click',
    function () {
        console.log(
            'Child'
        );
    }
);
Output After Clicking Button

stopPropagation()

The stopPropagation() method prevents the event from continuing through the remaining propagation path.

Stop Bubbling
child.addEventListener(
    'click',
    function (event) {
        event.stopPropagation();

        console.log(
            'Child'
        );
    }
);


parent.addEventListener(
    'click',
    function () {
        console.log(
            'Parent'
        );
    }
);
Output

stopImmediatePropagation()

The stopImmediatePropagation() method stops propagation and also prevents later listeners on the same element from running.

Stop Immediate Propagation
button.addEventListener(
    'click',
    function (event) {
        event.stopImmediatePropagation();

        console.log(
            'First'
        );
    }
);


button.addEventListener(
    'click',
    function () {
        console.log(
            'Second'
        );
    }
);
Output

Event Delegation

Event delegation is a technique where one event listener is attached to a parent element instead of attaching separate listeners to many child elements. The parent uses event bubbling to detect events from descendants.

index.html
<ul id="menu">
    <li>Home</li>
    <li>Courses</li>
    <li>Projects</li>
</ul>
Delegated Listener
const menu =
    document.getElementById(
        'menu'
    );

menu.addEventListener(
    'click',
    function (event) {
        console.log(
            event.target
        );
    }
);
Delegation Flow
User Clicks <li>

      │
      ▼

Event Starts on <li>

      │
      ▼

Event Bubbles to <ul>

      │
      ▼

Parent Listener Runs

      │
      ▼

event.target identifies <li>

Why Use Event Delegation?

Fewer Listeners

One parent listener can manage many child elements.

Dynamic Elements

New child elements can work without adding new listeners.

Simpler Code

Centralizes related event handling logic.

Scalable

Works well for large lists, tables, menus, and grids.

Delegation with target

Check Event Target
const list =
    document.getElementById(
        'itemList'
    );

list.addEventListener(
    'click',
    function (event) {
        if (
            event.target.matches(
                '.delete-button'
            )
        ) {
            console.log(
                'Delete clicked'
            );
        }
    }
);

Delegation with closest()

A user may click an element nested inside a button. closest() can find the nearest matching ancestor and make delegated handlers more reliable.

index.html
<button class="delete-button">
    <span>🗑️</span>
    <span>Delete</span>
</button>
Use closest()
container.addEventListener(
    'click',
    function (event) {
        const deleteButton =
            event.target.closest(
                '.delete-button'
            );


        if (!deleteButton) {
            return;
        }


        console.log(
            'Delete clicked'
        );
    }
);

Dynamic Elements

Event delegation is especially useful when elements are created after the original page has loaded.

Dynamic List with Delegation
const list =
    document.getElementById(
        'list'
    );


list.addEventListener(
    'click',
    function (event) {
        const item =
            event.target.closest(
                'li'
            );


        if (!item) {
            return;
        }


        item.classList.toggle(
            'selected'
        );
    }
);


const newItem =
    document.createElement(
        'li'
    );

newItem.textContent =
    'Dynamic Item';

list.append(newItem);

The dynamically created item works with the existing parent listener because its events bubble to the parent.

Listener Options

The third argument of addEventListener() can be an options object that changes listener behavior.

Listener Options
element.addEventListener(
    'click',
    handler,
    {
        capture: false,
        once: false,
        passive: false
    }
);

once Option

The once option automatically removes the listener after it runs for the first time.

Run Once
button.addEventListener(
    'click',
    function () {
        console.log(
            'Runs only once'
        );
    },
    {
        once: true
    }
);

capture Option

Capture Listener
parent.addEventListener(
    'click',
    handleClick,
    {
        capture: true
    }
);

When capture is true, the listener participates during the capturing phase instead of the default bubbling phase.

passive Option

A passive listener promises that it will not call preventDefault(). This can help browsers optimize some scrolling-related interactions.

Passive Listener
window.addEventListener(
    'scroll',
    function () {
        console.log(
            window.scrollY
        );
    },
    {
        passive: true
    }
);

AbortSignal Option

An AbortSignal can be used to remove one or more event listeners together.

Remove Listener with AbortController
const controller =
    new AbortController();


button.addEventListener(
    'click',
    function () {
        console.log(
            'Clicked'
        );
    },
    {
        signal:
            controller.signal
    }
);


// Remove listener
controller.abort();

Page and Document Events

The browser provides events related to document parsing, resource loading, navigation, and page lifecycle.

DOMContentLoaded Event

The DOMContentLoaded event occurs after the HTML document has been completely parsed. It does not wait for every image and external resource to finish loading.

DOMContentLoaded
document.addEventListener(
    'DOMContentLoaded',
    function () {
        console.log(
            'DOM is ready'
        );
    }
);

load Event

The load event on window occurs after the document and dependent resources have finished loading.

Window Load
window.addEventListener(
    'load',
    function () {
        console.log(
            'Page fully loaded'
        );
    }
);
DOMContentLoaded vs load
DOMContentLoaded

Waits For:
HTML Parsing

Does Not Necessarily Wait For:
Images
All external resources


load

Waits For:
Document
Images
Stylesheets
Dependent resources

beforeunload Event

The beforeunload event can be used in limited situations to warn users about leaving a page with unsaved changes. Modern browsers restrict custom messages.

Unsaved Changes Warning
let hasUnsavedChanges =
    true;


window.addEventListener(
    'beforeunload',
    function (event) {
        if (
            hasUnsavedChanges
        ) {
            event.preventDefault();

            event.returnValue = '';
        }
    }
);

Window Events

The window object receives events related to the browser viewport and page-level behavior.

resize Event

Window Resize
window.addEventListener(
    'resize',
    function () {
        console.log(
            window.innerWidth,
            window.innerHeight
        );
    }
);
High-Frequency Event
  • resize may fire repeatedly while the window size changes.
  • Avoid expensive calculations on every event.
  • Complex applications may use throttling or debouncing.

scroll Event

Window Scroll
window.addEventListener(
    'scroll',
    function () {
        console.log(
            window.scrollY
        );
    }
);

The scroll event is useful for progress indicators, sticky interfaces, lazy behavior, and other scroll-based features.

Clipboard Events

Clipboard events occur when users copy, cut, or paste content.

copy Event

Copy Event
document.addEventListener(
    'copy',
    function () {
        console.log(
            'Content copied'
        );
    }
);

cut Event

Cut Event
document.addEventListener(
    'cut',
    function () {
        console.log(
            'Content cut'
        );
    }
);

paste Event

Read Pasted Text
const input =
    document.querySelector(
        'input'
    );

input.addEventListener(
    'paste',
    function (event) {
        const text =
            event.clipboardData.getData(
                'text'
            );

        console.log(text);
    }
);

Drag and Drop Events

The browser provides events for implementing drag-and-drop interfaces.

Common Drag Events
dragstart

drag

dragend

dragenter

dragover

dragleave

drop
index.html
<div
    id="card"
    draggable="true"
>
    Drag Me
</div>

<div id="dropZone">
    Drop Here
</div>
Basic Drag and Drop
const card =
    document.getElementById(
        'card'
    );

const dropZone =
    document.getElementById(
        'dropZone'
    );


card.addEventListener(
    'dragstart',
    function () {
        console.log(
            'Drag started'
        );
    }
);


dropZone.addEventListener(
    'dragover',
    function (event) {
        event.preventDefault();
    }
);


dropZone.addEventListener(
    'drop',
    function () {
        dropZone.append(card);
    }
);

Custom Events

JavaScript applications can create their own events. Custom events allow different parts of an application to communicate through event-based patterns.

CustomEvent()

Create Custom Event
const courseCompleted =
    new CustomEvent(
        'courseCompleted',
        {
            detail: {
                course:
                    'JavaScript',
                lessons:
                    27
            }
        }
    );

dispatchEvent()

Listen and Dispatch
document.addEventListener(
    'courseCompleted',
    function (event) {
        console.log(
            event.detail.course
        );

        console.log(
            event.detail.lessons
        );
    }
);


document.dispatchEvent(
    courseCompleted
);
Output

Event Handler Context

The behavior of this inside an event handler depends on the type of function used.

The this Keyword in Events

Regular Function
button.addEventListener(
    'click',
    function () {
        console.log(
            this
        );
    }
);

Inside a regular function registered with addEventListener(), this refers to the current element receiving the listener and is equivalent to event.currentTarget.

Arrow Function
button.addEventListener(
    'click',
    () => {
        console.log(
            this
        );
    }
);

Arrow functions do not create their own this value, so this does not automatically refer to the element.

Prefer currentTarget for Clarity
  • event.currentTarget clearly identifies the element whose listener is running.
  • It works consistently with regular and arrow functions.
  • It avoids confusion about this behavior.

Complete Events Example

The following example creates an interactive course dashboard. Users can search lessons, filter lessons by status, mark lessons as completed, remove lessons, add new lessons, use keyboard shortcuts, and view live statistics. The application demonstrates event listeners, event objects, input events, form events, keyboard events, event delegation, preventDefault(), dataset, closest(), and dynamic DOM updates.

index.html
<div class="lesson-dashboard">
    <header class="dashboard-header">
        <p class="eyebrow">
            JavaScript Events Project
        </p>

        <h2>
            Course Lesson Manager
        </h2>

        <p>
            Search, filter, add, complete,
            and remove lessons.
        </p>
    </header>

    <div class="dashboard-controls">
        <input
            id="searchInput"
            type="search"
            placeholder="Search lessons..."
        >

        <select id="statusFilter">
            <option value="all">
                All Lessons
            </option>

            <option value="pending">
                Pending
            </option>

            <option value="completed">
                Completed
            </option>
        </select>
    </div>

    <form id="lessonForm">
        <input
            id="lessonInput"
            name="lesson"
            type="text"
            placeholder="Enter lesson title"
        >

        <button type="submit">
            Add Lesson
        </button>
    </form>

    <div
        id="message"
        class="dashboard-message"
    >
        Ready to manage your lessons.
    </div>

    <ul id="lessonList">
        <li
            class="lesson-item"
            data-status="pending"
        >
            <div class="lesson-content">
                <h3>
                    JavaScript DOM
                </h3>

                <span>
                    Pending
                </span>
            </div>

            <div class="lesson-actions">
                <button
                    type="button"
                    class="complete-button"
                >
                    <span>✓</span>
                    Complete
                </button>

                <button
                    type="button"
                    class="delete-button"
                >
                    <span>🗑️</span>
                    Delete
                </button>
            </div>
        </li>

        <li
            class="lesson-item"
            data-status="pending"
        >
            <div class="lesson-content">
                <h3>
                    JavaScript Events
                </h3>

                <span>
                    Pending
                </span>
            </div>

            <div class="lesson-actions">
                <button
                    type="button"
                    class="complete-button"
                >
                    <span>✓</span>
                    Complete
                </button>

                <button
                    type="button"
                    class="delete-button"
                >
                    <span>🗑️</span>
                    Delete
                </button>
            </div>
        </li>
    </ul>

    <div class="dashboard-stats">
        <div>
            <span>Total</span>

            <strong id="totalCount">
                0
            </strong>
        </div>

        <div>
            <span>Pending</span>

            <strong id="pendingCount">
                0
            </strong>
        </div>

        <div>
            <span>Completed</span>

            <strong id="completedCount">
                0
            </strong>
        </div>
    </div>

    <p class="keyboard-tip">
        Shortcut:
        Press Ctrl + K to focus search.
    </p>
</div>
style.css
.lesson-dashboard {
    max-width: 820px;
    margin: 40px auto;
    padding: 28px;
    border-radius: 18px;
    background: var(--bg-secondary);
}

.dashboard-header {
    margin-bottom: 24px;
}

.eyebrow {
    margin-bottom: 8px;
    color: var(--accent-light);
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.dashboard-controls {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 10px;
    margin-bottom: 12px;
}

.dashboard-controls input,
.dashboard-controls select,
#lessonForm input,
#lessonForm button {
    padding: 12px 14px;
    border-radius: 10px;
}

#lessonForm {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 10px;
    margin-bottom: 16px;
}

.dashboard-message {
    margin-bottom: 16px;
    color: var(--text-secondary);
}

#lessonList {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 0;
    list-style: none;
}

.lesson-item {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 16px;
    border: 1px solid var(--border-color);
    border-radius: 12px;
}

.lesson-content {
    flex: 1;
}

.lesson-content h3 {
    margin: 0 0 6px;
}

.lesson-content span {
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.lesson-item.completed {
    opacity: 0.75;
}

.lesson-item.completed h3 {
    text-decoration: line-through;
}

.lesson-actions {
    display: flex;
    gap: 8px;
}

.lesson-actions button {
    padding: 8px 10px;
    border: none;
    border-radius: 8px;
    cursor: pointer;
}

.dashboard-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    margin-top: 20px;
}

.dashboard-stats div {
    padding: 14px;
    border-radius: 12px;
    background: var(--bg-primary);
}

.dashboard-stats span,
.dashboard-stats strong {
    display: block;
}

.dashboard-stats span {
    margin-bottom: 6px;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.keyboard-tip {
    margin-top: 18px;
    font-size: 0.8rem;
    color: var(--text-secondary);
}
script.js
const searchInput =
    document.getElementById(
        'searchInput'
    );

const statusFilter =
    document.getElementById(
        'statusFilter'
    );

const lessonForm =
    document.getElementById(
        'lessonForm'
    );

const lessonInput =
    document.getElementById(
        'lessonInput'
    );

const lessonList =
    document.getElementById(
        'lessonList'
    );

const message =
    document.getElementById(
        'message'
    );

const totalCount =
    document.getElementById(
        'totalCount'
    );

const pendingCount =
    document.getElementById(
        'pendingCount'
    );

const completedCount =
    document.getElementById(
        'completedCount'
    );


searchInput.addEventListener(
    'input',
    filterLessons
);


statusFilter.addEventListener(
    'change',
    filterLessons
);


lessonForm.addEventListener(
    'submit',
    function (event) {
        event.preventDefault();

        const title =
            lessonInput.value.trim();


        if (title === '') {
            showMessage(
                'Enter a lesson title.'
            );

            lessonInput.focus();

            return;
        }


        const lesson =
            createLesson(title);


        lessonList.append(
            lesson
        );


        lessonInput.value = '';

        lessonInput.focus();


        showMessage(
            'Lesson added successfully.'
        );


        updateStatistics();

        filterLessons();
    }
);


lessonList.addEventListener(
    'click',
    function (event) {
        const completeButton =
            event.target.closest(
                '.complete-button'
            );

        const deleteButton =
            event.target.closest(
                '.delete-button'
            );


        if (completeButton) {
            const lesson =
                completeButton.closest(
                    '.lesson-item'
                );

            toggleLesson(
                lesson,
                completeButton
            );

            return;
        }


        if (deleteButton) {
            const lesson =
                deleteButton.closest(
                    '.lesson-item'
                );

            const title =
                lesson.querySelector(
                    'h3'
                ).textContent;


            lesson.remove();


            showMessage(
                `${title} removed.`
            );


            updateStatistics();

            filterLessons();
        }
    }
);


document.addEventListener(
    'keydown',
    function (event) {
        if (
            event.ctrlKey
            &&
            event.key.toLowerCase()
                === 'k'
        ) {
            event.preventDefault();

            searchInput.focus();

            showMessage(
                'Search focused.'
            );
        }


        if (
            event.key === 'Escape'
        ) {
            searchInput.value = '';

            statusFilter.value =
                'all';

            filterLessons();

            showMessage(
                'Filters cleared.'
            );
        }
    }
);


function createLesson(title) {
    const lesson =
        document.createElement(
            'li'
        );

    lesson.classList.add(
        'lesson-item'
    );

    lesson.dataset.status =
        'pending';


    const content =
        document.createElement(
            'div'
        );

    content.classList.add(
        'lesson-content'
    );


    const heading =
        document.createElement(
            'h3'
        );

    heading.textContent =
        title;


    const status =
        document.createElement(
            'span'
        );

    status.textContent =
        'Pending';


    content.append(
        heading,
        status
    );


    const actions =
        document.createElement(
            'div'
        );

    actions.classList.add(
        'lesson-actions'
    );


    const completeButton =
        document.createElement(
            'button'
        );

    completeButton.type =
        'button';

    completeButton.classList.add(
        'complete-button'
    );

    completeButton.innerHTML =
        '<span>✓</span> Complete';


    const deleteButton =
        document.createElement(
            'button'
        );

    deleteButton.type =
        'button';

    deleteButton.classList.add(
        'delete-button'
    );

    deleteButton.innerHTML =
        '<span>🗑️</span> Delete';


    actions.append(
        completeButton,
        deleteButton
    );


    lesson.append(
        content,
        actions
    );


    return lesson;
}


function toggleLesson(
    lesson,
    button
) {
    const isCompleted =
        lesson.classList.toggle(
            'completed'
        );


    const status =
        lesson.querySelector(
            '.lesson-content span'
        );


    if (isCompleted) {
        lesson.dataset.status =
            'completed';

        status.textContent =
            'Completed';

        button.innerHTML =
            '<span>↶</span> Undo';

        showMessage(
            'Lesson completed.'
        );
    } else {
        lesson.dataset.status =
            'pending';

        status.textContent =
            'Pending';

        button.innerHTML =
            '<span>✓</span> Complete';

        showMessage(
            'Lesson moved to pending.'
        );
    }


    updateStatistics();

    filterLessons();
}


function filterLessons() {
    const searchTerm =
        searchInput.value
            .trim()
            .toLowerCase();

    const selectedStatus =
        statusFilter.value;

    const lessons =
        lessonList.querySelectorAll(
            '.lesson-item'
        );


    lessons.forEach(
        function (lesson) {
            const title =
                lesson.querySelector(
                    'h3'
                )
                .textContent
                .toLowerCase();

            const status =
                lesson.dataset.status;


            const matchesSearch =
                title.includes(
                    searchTerm
                );

            const matchesStatus =
                selectedStatus === 'all'
                ||
                selectedStatus === status;


            lesson.hidden =
                !(
                    matchesSearch
                    &&
                    matchesStatus
                );
        }
    );
}


function updateStatistics() {
    const lessons =
        lessonList.querySelectorAll(
            '.lesson-item'
        );

    const completed =
        lessonList.querySelectorAll(
            '.lesson-item.completed'
        );


    const total =
        lessons.length;

    const completedTotal =
        completed.length;

    const pendingTotal =
        total - completedTotal;


    totalCount.textContent =
        total;

    pendingCount.textContent =
        pendingTotal;

    completedCount.textContent =
        completedTotal;
}


function showMessage(text) {
    message.textContent =
        text;
}


updateStatistics();
Browser Output
What This Example Demonstrates
  • Selecting DOM elements.
  • Registering event listeners.
  • Using the input event.
  • Using the change event.
  • Using the submit event.
  • Using the click event.
  • Using the keydown event.
  • Receiving the event object.
  • Calling preventDefault().
  • Reading keyboard modifier keys.
  • Reading event.key.
  • Creating keyboard shortcuts.
  • Using event delegation.
  • Using event.target.
  • Using closest() with delegated events.
  • Handling clicks on nested button content.
  • Creating dynamic elements.
  • Using data attributes.
  • Reading dataset values.
  • Toggling CSS classes.
  • Updating text content.
  • Adding and removing elements.
  • Filtering visible elements.
  • Updating live statistics.
  • Managing dynamically created elements without adding individual click listeners.
  • Combining DOM manipulation and events into a complete interactive application.

Event Processing Flow

Event Processing Flow
APPLICATION STARTS

        │
        ▼

┌──────────────────────────────┐
│ SELECT ELEMENTS              │
│                              │
│ Buttons                      │
│ Inputs                       │
│ Forms                        │
│ Containers                   │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ REGISTER LISTENERS           │
│                              │
│ click                        │
│ input                        │
│ submit                       │
│ keydown                      │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ WAIT FOR EVENTS              │
│                              │
│ Application remains ready    │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ EVENT OCCURS                 │
│                              │
│ User or browser action       │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ EVENT OBJECT CREATED         │
│                              │
│ type                         │
│ target                       │
│ currentTarget                │
│ key                          │
│ coordinates                  │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ PROPAGATION                  │
│                              │
│ Capturing                    │
│ Target                       │
│ Bubbling                     │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ HANDLER RUNS                 │
│                              │
│ Read data                    │
│ Validate                     │
│ Process logic                │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│ APPLICATION RESPONDS         │
│                              │
│ Update DOM                   │
│ Change classes               │
│ Create elements              │
│ Remove elements              │
└──────────────────────────────┘

Real-World Applications

Form Validation

Validate fields while users type and when forms are submitted.

Live Search

Filter content immediately using input events.

Shopping Carts

Handle add, remove, quantity, and checkout actions.

Task Managers

Respond to task creation, completion, filtering, and deletion.

Navigation Menus

Open and close responsive menus.

Modals

Open dialogs, close them with buttons, and respond to Escape.

Tabs

Switch visible panels when users select tabs.

Accordions

Expand and collapse sections.

Browser Games

Respond to keyboard and pointer controls.

Keyboard Shortcuts

Create application commands using key combinations.

Drag and Drop

Move cards, files, and interface elements.

Dashboards

Respond to filters, selections, resizing, and scrolling.

Common Beginner Mistakes

Avoid These Mistakes
  • Writing onclick instead of click in addEventListener().
  • Writing onsubmit instead of submit.
  • Calling a handler instead of passing the function.
  • Writing addEventListener("click", handleClick()).
  • Forgetting that the browser calls the handler later.
  • Using inline event handlers everywhere.
  • Mixing large amounts of JavaScript into HTML attributes.
  • Replacing an onclick handler accidentally.
  • Expecting onclick to support multiple assigned handlers.
  • Forgetting to select the element before adding a listener.
  • Calling addEventListener() on null.
  • Ignoring the possibility that an optional element does not exist.
  • Using anonymous functions when the listener must later be removed.
  • Trying to remove a listener with a different function object.
  • Forgetting that identical function code does not mean identical function identity.
  • Removing a listener with the wrong event type.
  • Confusing event.target and event.currentTarget.
  • Assuming target always refers to the element with the listener.
  • Forgetting that clicks can originate from nested elements.
  • Using target directly in delegation without checking what was clicked.
  • Ignoring closest() when buttons contain icons or spans.
  • Forgetting that closest() can return null.
  • Using mouseover when mouseenter behavior is actually required.
  • Confusing mouseout and mouseleave.
  • Performing expensive work on every mousemove event.
  • Performing expensive work on every scroll event.
  • Performing expensive work on every resize event.
  • Using deprecated keyboard properties such as keyCode for new code.
  • Confusing event.key and event.code.
  • Comparing event.key with incorrect capitalization.
  • Forgetting that Shift can change the value of event.key.
  • Ignoring modifier keys when implementing shortcuts.
  • Overriding common browser shortcuts without a strong reason.
  • Forgetting preventDefault() on custom form handling.
  • Adding a click listener only to the submit button instead of listening for form submission.
  • Forgetting that pressing Enter can submit a form.
  • Reading input values without trimming when whitespace should be ignored.
  • Forgetting that form input values are strings.
  • Using the input event when only a committed change is needed.
  • Using the change event when immediate feedback is required.
  • Confusing focus and focusin.
  • Confusing blur and focusout.
  • Expecting focus and blur to bubble normally.
  • Calling preventDefault() when no default behavior needs to be prevented.
  • Assuming preventDefault() stops propagation.
  • Assuming stopPropagation() prevents default browser behavior.
  • Confusing preventDefault() and stopPropagation().
  • Using stopPropagation() unnecessarily.
  • Breaking event delegation by stopping propagation without understanding the effect.
  • Assuming only the target element receives the event.
  • Ignoring event capturing and bubbling.
  • Expecting parent bubbling listeners to run before target listeners.
  • Using capture accidentally.
  • Passing true as the third argument without understanding capturing.
  • Using stopImmediatePropagation() without understanding that it blocks later listeners on the same element.
  • Adding one listener to every item when delegation would be simpler.
  • Forgetting that dynamically created elements do not automatically receive individually attached old listeners.
  • Using event delegation on a parent that does not contain the target elements.
  • Failing to verify that a delegated match belongs to the intended container.
  • Using innerHTML with untrusted content inside event handlers.
  • Creating duplicate event listeners during repeated rendering.
  • Registering the same listener every time a modal opens.
  • Forgetting to remove listeners that are no longer required.
  • Creating memory and behavior problems with unnecessary long-lived listeners.
  • Using passive listeners and then calling preventDefault().
  • Expecting preventDefault() to work inside a passive listener.
  • Using beforeunload for ordinary navigation logic.
  • Expecting custom beforeunload messages to display exactly as written.
  • Confusing DOMContentLoaded with window load.
  • Waiting for load when only parsed HTML is required.
  • Using DOMContentLoaded unnecessarily when a deferred script already runs after parsing.
  • Forgetting to prevent default behavior during dragover when implementing a drop zone.
  • Assuming custom events occur automatically.
  • Creating a CustomEvent but forgetting to dispatch it.
  • Dispatching an event on the wrong object.
  • Expecting arrow-function this to refer to the event element.
  • Using this when currentTarget would be clearer.
  • Creating event handlers that perform too many unrelated tasks.
  • Mixing validation, data processing, rendering, and networking in one large handler.
  • Failing to provide keyboard access for interactions that only respond to mouse clicks.
  • Using non-semantic clickable div elements instead of buttons.
  • Removing default behavior without providing an accessible replacement.
  • Ignoring touch and pointer interaction requirements.
  • Not testing nested element clicks.
  • Not testing keyboard interaction.
  • Not testing dynamically added elements.
  • Not testing repeated events.
  • Not testing empty forms.
  • Not testing missing DOM elements.
Common Event Mistakes
// ❌ Wrong event name
button.addEventListener(
    'onclick',
    handleClick
);


// ✅ Correct
button.addEventListener(
    'click',
    handleClick
);


// ❌ Calls immediately
button.addEventListener(
    'click',
    handleClick()
);


// ✅ Pass function reference
button.addEventListener(
    'click',
    handleClick
);


// ❌ Different function objects
button.addEventListener(
    'click',
    function () {
        console.log('Click');
    }
);

button.removeEventListener(
    'click',
    function () {
        console.log('Click');
    }
);


// ✅ Same function reference
function handleClick() {
    console.log('Click');
}

button.addEventListener(
    'click',
    handleClick
);

button.removeEventListener(
    'click',
    handleClick
);


// ❌ Wrong for form handling
submitButton.addEventListener(
    'click',
    handleSubmit
);


// ✅ Listen for form submission
form.addEventListener(
    'submit',
    handleSubmit
);


// ❌ target may be nested span
if (
    event.target.classList.contains(
        'delete-button'
    )
) {
    // ...
}


// ✅ More reliable
const deleteButton =
    event.target.closest(
        '.delete-button'
    );

if (deleteButton) {
    // ...
}


// ❌ preventDefault does not stop bubbling
event.preventDefault();


// ✅ Stops propagation
event.stopPropagation();

Best Practices

  • Understand events as browser notifications that something has happened.
  • Use addEventListener() for general event handling.
  • Use event names without the on prefix.
  • Use click instead of onclick with addEventListener().
  • Pass function references instead of calling handlers immediately.
  • Use named functions when handlers are reusable.
  • Use named functions when listeners may need to be removed.
  • Use anonymous functions for small local behavior.
  • Use arrow functions when their lexical this behavior is appropriate.
  • Use event.currentTarget instead of this when clarity is important.
  • Check whether optional elements exist before adding listeners.
  • Store frequently used element references.
  • Avoid repeatedly querying the same static elements.
  • Keep event handlers focused on one responsibility.
  • Move complex logic into separate functions.
  • Use descriptive handler names such as handleSubmit and handleDelete.
  • Use the event object to understand what happened.
  • Use event.type when one handler manages multiple event types.
  • Use event.target to identify where an event originated.
  • Use event.currentTarget to identify whose listener is running.
  • Understand the difference between target and currentTarget.
  • Use mouseenter and mouseleave for simple hover-boundary behavior.
  • Understand that mouseover and mouseout bubble.
  • Avoid expensive work in high-frequency events.
  • Use key for semantic keyboard input.
  • Use code when physical keyboard position matters.
  • Use modern keyboard properties instead of deprecated keyCode.
  • Check modifier properties for keyboard shortcuts.
  • Avoid overriding standard browser shortcuts unnecessarily.
  • Use input for immediate value changes.
  • Use change when a committed change is appropriate.
  • Use focus and blur for direct element focus behavior.
  • Use focusin and focusout when bubbling is required.
  • Listen for submit on the form rather than only click on the submit button.
  • Use preventDefault() when custom form handling replaces browser submission.
  • Use FormData for convenient form value collection.
  • Give form controls name attributes when using FormData.
  • Understand the default action before preventing it.
  • Do not confuse preventDefault() with stopPropagation().
  • Understand capturing, target, and bubbling phases.
  • Use stopPropagation() only when propagation genuinely must stop.
  • Use stopImmediatePropagation() carefully.
  • Prefer event delegation for large groups of similar elements.
  • Use delegation for dynamically created elements.
  • Use matches() for direct selector checks.
  • Use closest() when interactive controls contain nested elements.
  • Check closest() results before using them.
  • Ensure delegated matches belong to the intended container.
  • Use the once option for one-time behavior.
  • Use capture only when capturing-phase behavior is required.
  • Use passive listeners when appropriate for non-canceling scroll-related behavior.
  • Do not call preventDefault() inside passive listeners.
  • Use AbortController when groups of listeners should be removed together.
  • Use DOMContentLoaded when code must wait for parsed HTML.
  • Use defer for external scripts that depend on the DOM.
  • Use load only when dependent resources must also be ready.
  • Use beforeunload only for genuine unsaved-data scenarios.
  • Avoid heavy work inside scroll and resize handlers.
  • Use semantic interactive elements such as buttons.
  • Preserve keyboard accessibility.
  • Do not make important functionality mouse-only.
  • Use event delegation carefully with nested interactive controls.
  • Avoid registering duplicate listeners during repeated rendering.
  • Remove temporary listeners when they are no longer needed.
  • Use custom events for meaningful application-level communication.
  • Include useful data in CustomEvent detail.
  • Dispatch custom events from the object that logically owns the event.
  • Separate event handling from DOM rendering when applications grow.
  • Test direct clicks.
  • Test clicks on nested icons and text.
  • Test keyboard navigation.
  • Test form submission with Enter.
  • Test dynamically created elements.
  • Test repeated interactions.
  • Test missing and empty states.
  • Keep event behavior predictable and accessible.

Frequently Asked Questions

What is a JavaScript event?

An event is a browser notification that something has happened, such as a click, key press, form submission, or page load.

What is an event handler?

An event handler is a function that runs in response to an event.

What is an event listener?

An event listener waits for a specific event and runs a handler when the event occurs.

What is event-driven programming?

It is a programming style where application behavior is triggered by events.

What is the recommended way to handle events?

addEventListener() is the recommended general-purpose approach.

Should I write click or onclick in addEventListener()?

Use click without the on prefix.

Can one element have multiple event listeners?

Yes. addEventListener() supports multiple listeners.

How do I remove an event listener?

Use removeEventListener() with the same event type and function reference.

Why does removeEventListener() sometimes fail?

The removal must use the same function object that was originally registered.

What is the event object?

It is an object created by the browser that contains information about the event.

What is event.target?

It is the element where the event originated.

What is event.currentTarget?

It is the element whose event listener is currently running.

What is the difference between target and currentTarget?

target identifies where the event started, while currentTarget identifies the element whose listener is executing.

What is the click event?

It occurs when an element is activated with a pointing device.

What is the difference between mousedown and click?

mousedown occurs when the button is pressed, while click occurs after the activation sequence completes.

What is the difference between mouseenter and mouseover?

mouseenter does not bubble and is less affected by movement between descendants, while mouseover bubbles.

What is the difference between mouseleave and mouseout?

mouseleave does not bubble, while mouseout bubbles and can occur when moving between descendants.

How do I get mouse coordinates?

Use properties such as clientX, clientY, pageX, and pageY.

What is the keydown event?

It occurs when a keyboard key is pressed.

What is the keyup event?

It occurs when a pressed keyboard key is released.

What is event.key?

It represents the meaning or value produced by the key.

What is event.code?

It represents the physical key position.

Should I use keyCode?

Modern code should generally use key and code instead of deprecated keyCode.

How do I detect Ctrl?

Use event.ctrlKey.

How do I detect Shift?

Use event.shiftKey.

How do I detect Alt?

Use event.altKey.

What is the input event?

It occurs as a form control value changes through user interaction.

What is the change event?

It occurs when a control value change is committed.

What is the difference between input and change?

input is useful for immediate updates, while change is useful for committed value changes.

What is the focus event?

It occurs when an element receives focus.

What is the blur event?

It occurs when an element loses focus.

Do focus and blur bubble?

They do not bubble in the same way as focusin and focusout.

What is the submit event?

It occurs when a form is submitted.

Should I listen for click on the submit button?

For form submission logic, listen for the submit event on the form.

What does preventDefault() do?

It prevents the browser default action associated with the event.

Does preventDefault() stop event bubbling?

No.

What does stopPropagation() do?

It prevents the event from continuing through the propagation path.

Does stopPropagation() prevent default behavior?

No.

What is event propagation?

It is the process through which an event travels across elements in the DOM hierarchy.

What are the event propagation phases?

Capturing, target, and bubbling.

What is event capturing?

It is the phase where the event travels from outer ancestors toward the target.

What is event bubbling?

It is the phase where the event travels from the target upward through ancestors.

Do event listeners use bubbling by default?

Most addEventListener() listeners use the bubbling phase by default.

How do I use the capturing phase?

Pass true or use the capture option.

What does stopImmediatePropagation() do?

It stops propagation and prevents later listeners on the same element from running.

What is event delegation?

It is a technique where a parent listener handles events from descendant elements.

Why is event delegation useful?

It reduces listener count and works well with dynamically created elements.

How does delegation know which child was clicked?

Use event.target, matches(), or closest().

Why use closest() in event delegation?

It can find a matching control even when the user clicks a nested icon or span.

Do dynamically created elements work with event delegation?

Yes, as long as their events bubble to the parent listener.

What does the once option do?

It automatically removes the listener after its first execution.

What does the capture option do?

It makes the listener participate during the capturing phase.

What does the passive option do?

It tells the browser that the listener will not call preventDefault().

What is DOMContentLoaded?

It fires after the HTML document has been parsed.

What is the window load event?

It fires after the document and dependent resources have loaded.

What is the difference between DOMContentLoaded and load?

DOMContentLoaded waits for HTML parsing, while load waits for the document and dependent resources.

What is the resize event?

It occurs when the browser viewport size changes.

What is the scroll event?

It occurs when a document or scrollable element is scrolled.

What are clipboard events?

They are events related to copy, cut, and paste operations.

What is a custom event?

It is an application-defined event created by JavaScript.

How do I create a custom event?

Use CustomEvent().

How do I trigger a custom event?

Use dispatchEvent().

How do I send data with a custom event?

Pass data through the detail property.

What does this refer to in a regular event handler?

For a regular function registered with addEventListener(), it refers to the element whose listener is running.

Does this work the same in an arrow event handler?

No. Arrow functions do not create their own this value.

What should I use instead of this for clarity?

Use event.currentTarget.

Key Takeaways

  • Events are notifications that something has happened.
  • Events may come from users, browsers, documents, or JavaScript.
  • JavaScript applications are often event-driven.
  • Event handlers are functions that respond to events.
  • addEventListener() is the recommended general-purpose event API.
  • Event names are written without the on prefix.
  • Multiple listeners can be registered for the same event.
  • removeEventListener() requires the same function reference.
  • The browser passes an event object to handlers.
  • event.type contains the event name.
  • event.target identifies where the event originated.
  • event.currentTarget identifies whose listener is running.
  • Mouse events respond to pointer interactions.
  • Keyboard events respond to key presses and releases.
  • event.key represents the key meaning or value.
  • event.code represents the physical key position.
  • Modifier keys are available through boolean event properties.
  • The input event is useful for immediate value changes.
  • The change event is useful for committed changes.
  • Focus events track focus movement.
  • The submit event should be handled on the form.
  • preventDefault() prevents a browser default action.
  • preventDefault() does not stop propagation.
  • Event propagation has capturing, target, and bubbling phases.
  • Most listeners run during bubbling by default.
  • stopPropagation() stops further propagation.
  • stopImmediatePropagation() also blocks later listeners on the same element.
  • Event delegation uses a parent listener to manage descendant events.
  • Delegation works well with dynamically created elements.
  • event.target helps identify the original source.
  • closest() makes delegated handlers more reliable.
  • The once option creates a one-time listener.
  • The capture option enables capturing-phase listening.
  • The passive option promises not to prevent the default action.
  • AbortSignal can remove groups of listeners.
  • DOMContentLoaded occurs after HTML parsing.
  • load occurs after the page and dependent resources load.
  • resize and scroll are high-frequency window events.
  • Clipboard events respond to copy, cut, and paste.
  • Drag-and-drop interfaces use dedicated drag events.
  • CustomEvent creates application-defined events.
  • dispatchEvent() triggers events programmatically.
  • Regular and arrow functions handle this differently.
  • event.currentTarget provides a clear reference to the listener element.
  • Events are the foundation of interactive web applications.

Summary

JavaScript events allow applications to respond to user actions and browser activity. Every click, key press, form submission, input change, focus movement, page load, scroll, and resize can produce an event.

The addEventListener() method is the primary way to register event handlers. It supports multiple listeners, listener removal, event propagation options, one-time listeners, passive behavior, and AbortSignal-based cleanup.

When an event occurs, the browser creates an event object containing information about what happened. Properties such as type, target, currentTarget, key, code, coordinates, and modifier states help handlers understand the event.

Mouse events respond to clicks and pointer movement, keyboard events respond to key presses, input events respond to value changes, focus events track focus movement, and form events handle submission and reset behavior.

The preventDefault() method stops a browser default action, while propagation methods control how events move through the DOM hierarchy. These concepts are different and should not be confused.

Events travel through capturing, target, and bubbling phases. Event bubbling makes event delegation possible, allowing one parent listener to manage many child elements, including elements created dynamically.

Listener options such as once, capture, passive, and signal provide additional control over how listeners behave and how they are removed.

Page, window, clipboard, drag-and-drop, and custom events support advanced browser interactions and application communication.

Mastering events allows you to build forms, menus, modals, tabs, search interfaces, shopping carts, dashboards, task managers, games, keyboard shortcuts, drag-and-drop systems, and complete interactive web applications.

Lesson 22 Completed
  • You understand what JavaScript events are.
  • You understand why interactive applications need events.
  • You understand event-driven programming.
  • You know the main event categories.
  • You understand different event handling approaches.
  • You can use addEventListener().
  • You understand why addEventListener() is recommended.
  • You can create named event handlers.
  • You can create anonymous event handlers.
  • You can use arrow function handlers.
  • You can register multiple listeners.
  • You can remove listeners.
  • You understand function identity in listener removal.
  • You understand the event object.
  • You can read event.type.
  • You understand event.target.
  • You understand event.currentTarget.
  • You know the difference between target and currentTarget.
  • You can handle click events.
  • You can handle double-click events.
  • You can handle mouse button events.
  • You can handle mouse movement.
  • You can handle mouse enter and leave behavior.
  • You can read mouse coordinates.
  • You can detect mouse buttons.
  • You can handle keydown events.
  • You can handle keyup events.
  • You understand event.key.
  • You understand event.code.
  • You can detect modifier keys.
  • You can create keyboard shortcuts.
  • You can handle input events.
  • You can handle change events.
  • You understand the difference between input and change.
  • You can handle focus events.
  • You can handle blur events.
  • You understand focusin and focusout.
  • You can handle form submissions.
  • You can read form controls.
  • You can use FormData.
  • You can handle form resets.
  • You can use preventDefault().
  • You understand default browser behavior.
  • You understand event propagation.
  • You understand event capturing.
  • You understand the target phase.
  • You understand event bubbling.
  • You can use stopPropagation().
  • You can use stopImmediatePropagation().
  • You understand event delegation.
  • You understand why delegation is useful.
  • You can use target in delegated handlers.
  • You can use closest() in delegated handlers.
  • You can handle dynamically created elements.
  • You understand listener options.
  • You can create one-time listeners.
  • You can use capture listeners.
  • You understand passive listeners.
  • You can remove listeners with AbortSignal.
  • You understand DOMContentLoaded.
  • You understand the load event.
  • You understand beforeunload.
  • You can handle resize events.
  • You can handle scroll events.
  • You understand clipboard events.
  • You understand drag-and-drop events.
  • You can create custom events.
  • You can dispatch custom events.
  • You understand this inside event handlers.
  • You can build a complete event-driven application.
  • You are ready to learn the Browser Object Model.
Next Lesson →

JavaScript BOM