Skip to content

Event Object: Treasure Trove of Event Information

What Is the Event Object

When you click a button, press a key, or move your mouse, the browser doesn't just simply trigger an event. It creates an object containing rich information, recording every aspect of the event: where it occurred, when it occurred, which keys or buttons were involved, which element it occurred on... This object is the Event Object.

Think of the event object as a detailed event report. Just as police record time, location, involved persons, and evidence when arriving at a crime scene, the event object records all data related to that event. With this report, our code can make precise responses based on specific circumstances.

Whenever an event handler or listener is called, the browser automatically creates an event object and passes it as the first parameter:

javascript
button.addEventListener("click", function (event) {
  // event is the event object
  console.log(event);
});

Universal Event Properties

All types of event objects inherit from the base Event interface and have some universal properties.

type - Event Type

The type property is a string representing the event type:

javascript
button.addEventListener("click", function (event) {
  console.log(event.type); // "click"
});

input.addEventListener("input", function (event) {
  console.log(event.type); // "input"
});

document.addEventListener("DOMContentLoaded", function (event) {
  console.log(event.type); // "DOMContentLoaded"
});

If the same handler is used to handle multiple events, you can distinguish based on type:

javascript
function handleEvent(event) {
  switch (event.type) {
    case "mouseenter":
      element.style.backgroundColor = "lightblue";
      break;
    case "mouseleave":
      element.style.backgroundColor = "";
      break;
    case "click":
      console.log("Element clicked");
      break;
  }
}

element.addEventListener("mouseenter", handleEvent);
element.addEventListener("mouseleave", handleEvent);
element.addEventListener("click", handleEvent);

target - Event Target

The target property points to the element that actually triggered the event, which is the element the user truly interacted with:

html
<div id="container">
  <button id="btn">
    <span>Click me</span>
  </button>
</div>
javascript
const container = document.getElementById("container");

container.addEventListener("click", function (event) {
  console.log("Clicked element:", event.target);
  // If clicking span: <span>Click me</span>
  // If clicking button: <button id="btn">...</button>
  // If clicking div's blank area: <div id="container">...</div>
});

target is particularly useful in event delegation, allowing you to determine which specific child element was clicked:

javascript
const list = document.querySelector(".todo-list");

list.addEventListener("click", function (event) {
  if (event.target.matches(".delete-btn")) {
    deleteItem(event.target);
  } else if (event.target.matches(".edit-btn")) {
    editItem(event.target);
  } else if (event.target.matches(".checkbox")) {
    toggleItem(event.target);
  }
});

currentTarget - Current Handling Element

currentTarget points to the element that the event handler is bound to, which is the element that called addEventListener:

html
<div id="outer">
  <div id="inner">
    <button id="btn">Click</button>
  </div>
</div>
javascript
const outer = document.getElementById("outer");

outer.addEventListener("click", function (event) {
  console.log("target:", event.target.id); // btn (actually clicked)
  console.log("currentTarget:", event.currentTarget.id); // outer (event bound to)
  console.log("this:", this.id); // outer (this points to currentTarget)
});

In regular functions, the this keyword is equivalent to event.currentTarget:

javascript
button.addEventListener("click", function (event) {
  console.log(this === event.currentTarget); // true
  console.log(this === event.target); // true (if clicking button directly)
});

But in arrow functions, this inherits from the outer scope and is unrelated to currentTarget:

javascript
button.addEventListener("click", (event) => {
  console.log(this === event.currentTarget); // false
  console.log(event.currentTarget); // button element
  console.log(this); // outer scope's this
});

timeStamp - Event Timestamp

The timeStamp property represents the time when the event was created, as milliseconds from page load:

javascript
document.addEventListener("click", function (event) {
  console.log("Event occurred at:", event.timeStamp, "ms");
});

You can use timestamps to calculate operation intervals:

javascript
let lastClickTime = 0;

button.addEventListener("click", function (event) {
  const interval = event.timeStamp - lastClickTime;

  if (interval < 300) {
    console.log("Double-click detected");
  }

  lastClickTime = event.timeStamp;
});

isTrusted - Is Trusted

The isTrusted property indicates whether the event was triggered by user operation (true) or created by script (false):

javascript
button.addEventListener("click", function (event) {
  if (event.isTrusted) {
    console.log("Genuine user click");
  } else {
    console.log("Script-triggered click");
  }
});

// User click: isTrusted is true
// Code trigger: isTrusted is false
button.click();

This property can be used to prevent malicious automatic script operations.

eventPhase - Event Phase

eventPhase indicates which phase of the event flow the event is currently in:

  • Event.NONE (0): Event not triggered
  • Event.CAPTURING_PHASE (1): Capture phase
  • Event.AT_TARGET (2): Target phase
  • Event.BUBBLING_PHASE (3): Bubble phase
javascript
element.addEventListener(
  "click",
  function (event) {
    console.log("Current phase:", event.eventPhase);

    switch (event.eventPhase) {
      case Event.CAPTURING_PHASE:
        console.log("Capture phase");
        break;
      case Event.AT_TARGET:
        console.log("Target phase");
        break;
      case Event.BUBBLING_PHASE:
        console.log("Bubble phase");
        break;
    }
  },
  true
); // Capture phase

bubbles - Whether Bubbles

The bubbles property indicates whether this event bubbles:

javascript
button.addEventListener("click", function (event) {
  console.log("Bubbles:", event.bubbles); // true
});

button.addEventListener("focus", function (event) {
  console.log("Bubbles:", event.bubbles); // false
});

Most mouse and keyboard events bubble, but some events (such as focus, blur, scroll) don't bubble.

cancelable - Whether Cancelable

The cancelable property indicates whether the event's default behavior can be canceled:

javascript
link.addEventListener("click", function (event) {
  console.log("Cancelable:", event.cancelable); // true

  if (event.cancelable) {
    event.preventDefault(); // Can prevent navigation
  }
});

Mouse Event Properties

Mouse event objects (MouseEvent) contain detailed information related to mouse operations.

Mouse Position

javascript
document.addEventListener("click", function (event) {
  // Coordinates relative to viewport
  console.log("clientX:", event.clientX);
  console.log("clientY:", event.clientY);

  // Coordinates relative to entire page (including scroll)
  console.log("pageX:", event.pageX);
  console.log("pageY:", event.pageY);

  // Coordinates relative to screen
  console.log("screenX:", event.screenX);
  console.log("screenY:", event.screenY);

  // Coordinates relative to target element
  console.log("offsetX:", event.offsetX);
  console.log("offsetY:", event.offsetY);
});

Coordinate System Comparison:

  • clientX/Y: Relative to browser window's visible area, excluding scroll
  • pageX/Y: Relative to entire document, including scrolled part
  • screenX/Y: Relative to entire screen
  • offsetX/Y: Relative to element that triggered the event

The most commonly used in practical applications are clientX/Y and pageX/Y:

javascript
// Create tooltip following mouse
document.addEventListener("mousemove", function (event) {
  const tooltip = document.getElementById("tooltip");
  tooltip.style.left = event.clientX + 10 + "px";
  tooltip.style.top = event.clientY + 10 + "px";
});

// Draw on canvas
canvas.addEventListener("click", function (event) {
  const rect = canvas.getBoundingClientRect();
  const x = event.clientX - rect.left;
  const y = event.clientY - rect.top;
  drawPoint(x, y);
});

Mouse Buttons

javascript
element.addEventListener("mousedown", function (event) {
  console.log("Button number:", event.button);

  switch (event.button) {
    case 0:
      console.log("Left button");
      break;
    case 1:
      console.log("Middle button (wheel)");
      break;
    case 2:
      console.log("Right button");
      break;
  }
});

element.addEventListener("click", function (event) {
  // buttons property is a bitmask, can detect multiple buttons pressed simultaneously
  console.log("Button state:", event.buttons);
  // 1 = left, 2 = right, 4 = middle
});

Modifier Keys

Detect whether Ctrl, Shift, Alt, or Meta (Windows key or Command key) were pressed simultaneously:

javascript
document.addEventListener("click", function (event) {
  console.log("Ctrl:", event.ctrlKey);
  console.log("Shift:", event.shiftKey);
  console.log("Alt:", event.altKey);
  console.log("Meta:", event.metaKey);

  if (event.ctrlKey && event.key === "s") {
    event.preventDefault();
    console.log("Ctrl+S shortcut");
  }
});

Practical applications:

javascript
// Multi-select functionality
itemList.addEventListener("click", function (event) {
  if (!event.target.matches(".item")) return;

  if (event.ctrlKey || event.metaKey) {
    // Ctrl/Cmd + click: add to selection
    toggleItemSelection(event.target);
  } else if (event.shiftKey) {
    // Shift + click: range selection
    selectRange(lastSelectedItem, event.target);
  } else {
    // Normal click: single selection
    selectSingleItem(event.target);
  }
});

// Right-click menu
element.addEventListener("contextmenu", function (event) {
  if (event.shiftKey) {
    // Shift + right-click: show browser default menu
    return;
  }

  event.preventDefault();
  showCustomMenu(event.clientX, event.clientY);
});

Movement Distance

movementX and movementY represent the mouse's movement distance relative to the previous event:

javascript
let totalMovement = 0;

document.addEventListener("mousemove", function (event) {
  const distance = Math.sqrt(event.movementX ** 2 + event.movementY ** 2);
  totalMovement += distance;
  console.log("Total movement distance:", totalMovement);
});

Keyboard Event Properties

Keyboard event objects (KeyboardEvent) contain key information.

key - Key Value

The key property returns a string representation of the pressed key:

javascript
document.addEventListener("keydown", function (event) {
  console.log("Key:", event.key);

  // Letters and numbers: 'a', 'b', '1', '2'
  // Special keys: 'Enter', 'Escape', 'Tab', 'Backspace'
  // Arrow keys: 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'
  // Function keys: 'F1', 'F2', ... 'F12'
  // Modifier keys: 'Control', 'Shift', 'Alt', 'Meta'
});

Practical applications:

javascript
// Shortcuts
document.addEventListener("keydown", function (event) {
  if (event.ctrlKey || event.metaKey) {
    switch (event.key) {
      case "s":
        event.preventDefault();
        saveDocument();
        break;
      case "z":
        event.preventDefault();
        undo();
        break;
      case "y":
        event.preventDefault();
        redo();
        break;
    }
  }

  if (event.key === "Escape") {
    closeModal();
  }
});

// Arrow key navigation
document.addEventListener("keydown", function (event) {
  switch (event.key) {
    case "ArrowUp":
      event.preventDefault();
      selectPreviousItem();
      break;
    case "ArrowDown":
      event.preventDefault();
      selectNextItem();
      break;
    case "Enter":
      confirmSelection();
      break;
  }
});

code - Physical Key Code

The code property returns the physical position code of the key, unaffected by keyboard layout:

javascript
document.addEventListener("keydown", function (event) {
  console.log("key:", event.key); // Changes based on layout
  console.log("code:", event.code); // Physical position remains same

  // US keyboard: key = 'q', code = 'KeyQ'
  // French keyboard: key = 'a', code = 'KeyQ' (same physical key)
});

In game development, code is commonly used to ensure consistent key mapping:

javascript
const keys = {};

document.addEventListener("keydown", function (event) {
  keys[event.code] = true;
});

document.addEventListener("keyup", function (event) {
  keys[event.code] = false;
});

function gameLoop() {
  if (keys["KeyW"]) {
    moveForward();
  }
  if (keys["KeyS"]) {
    moveBackward();
  }
  if (keys["KeyA"]) {
    moveLeft();
  }
  if (keys["KeyD"]) {
    moveRight();
  }

  requestAnimationFrame(gameLoop);
}

repeat - Whether Repeating

The repeat property indicates whether the key press is a repeated trigger due to long press:

javascript
document.addEventListener("keydown", function (event) {
  if (event.repeat) {
    console.log("Key repeat");
    return; // Ignore repeat events
  }

  console.log("First press");
});

Methods for Controlling Event Behavior

The event object provides several methods to control event propagation and default behavior.

preventDefault() - Prevent Default Behavior

Many events have browser-preset default behaviors; preventDefault() can prevent these behaviors:

javascript
// Prevent form submission
form.addEventListener("submit", function (event) {
  event.preventDefault();

  // Use AJAX to submit
  const formData = new FormData(event.target);
  submitFormViaAjax(formData);
});

// Prevent link navigation
link.addEventListener("click", function (event) {
  event.preventDefault();

  // Use router navigation
  navigateTo(event.target.href);
});

// Prevent right-click menu
element.addEventListener("contextmenu", function (event) {
  event.preventDefault();

  // Show custom menu
  showCustomMenu(event.clientX, event.clientY);
});

// Prevent drag default behavior
dropZone.addEventListener("dragover", function (event) {
  event.preventDefault(); // Allow dropping
});

dropZone.addEventListener("drop", function (event) {
  event.preventDefault(); // Prevent browser from opening file

  const files = event.dataTransfer.files;
  handleFiles(files);
});

stopPropagation() - Stop Propagation

stopPropagation() prevents the event from continuing to propagate (bubble or capture):

javascript
button.addEventListener("click", function (event) {
  event.stopPropagation(); // Event won't bubble to parent elements
  console.log("Only this handler executes");
});

document.addEventListener("click", function () {
  // If clicking button, this handler won't execute
  console.log("Document clicked");
});

Typical application scenarios:

javascript
// Modal internal click doesn't close
const modal = document.querySelector(".modal");
const modalContent = modal.querySelector(".modal-content");

modal.addEventListener("click", function () {
  closeModal(); // Clicking background closes
});

modalContent.addEventListener("click", function (event) {
  event.stopPropagation(); // Clicking content doesn't close
});

// Dropdown menu
const dropdown = document.querySelector(".dropdown");
const menu = dropdown.querySelector(".menu");

dropdown.addEventListener("click", function (event) {
  event.stopPropagation();
  toggleMenu();
});

document.addEventListener("click", function () {
  closeAllMenus(); // Clicking elsewhere closes all menus
});

stopImmediatePropagation() - Stop Immediately

stopImmediatePropagation() not only prevents event propagation but also prevents other listeners on the same element from executing:

javascript
button.addEventListener("click", function (event) {
  console.log("First listener");
  event.stopImmediatePropagation();
});

button.addEventListener("click", function () {
  console.log("Second listener"); // Won't execute
});

button.addEventListener("click", function () {
  console.log("Third listener"); // Won't execute
});

// After clicking, only outputs: First listener

While stopPropagation() only prevents propagation and doesn't affect other listeners on the same element:

javascript
button.addEventListener("click", function (event) {
  console.log("First listener");
  event.stopPropagation();
});

button.addEventListener("click", function () {
  console.log("Second listener"); // Still executes
});

// After clicking, outputs:
// First listener
// Second listener

defaultPrevented - Check If Prevented

The defaultPrevented property can check whether preventDefault() has been called:

javascript
link.addEventListener("click", function (event) {
  if (!event.defaultPrevented) {
    // Other listeners haven't prevented default behavior
    console.log("Will navigate");
  } else {
    console.log("Navigation prevented");
  }
});

Real-World Application Scenarios

Custom Context Menu

javascript
const contextMenu = document.getElementById("context-menu");

document.addEventListener("contextmenu", function (event) {
  event.preventDefault();

  // Position menu
  contextMenu.style.left = event.clientX + "px";
  contextMenu.style.top = event.clientY + "px";
  contextMenu.style.display = "block";

  // Store target element
  contextMenu.dataset.target = event.target.id;
});

// Click elsewhere to close menu
document.addEventListener("click", function () {
  contextMenu.style.display = "none";
});

// Menu item click
contextMenu.addEventListener("click", function (event) {
  event.stopPropagation();

  const action = event.target.dataset.action;
  const targetId = this.dataset.target;
  const targetElement = document.getElementById(targetId);

  switch (action) {
    case "copy":
      copyElement(targetElement);
      break;
    case "delete":
      deleteElement(targetElement);
      break;
    case "edit":
      editElement(targetElement);
      break;
  }

  this.style.display = "none";
});

Drag-and-Drop Sorting

javascript
let draggedElement = null;

const items = document.querySelectorAll(".sortable-item");

items.forEach((item) => {
  item.addEventListener("dragstart", function (event) {
    draggedElement = this;
    event.dataTransfer.effectAllowed = "move";
    this.classList.add("dragging");
  });

  item.addEventListener("dragend", function () {
    this.classList.remove("dragging");
  });

  item.addEventListener("dragover", function (event) {
    event.preventDefault(); // Allow dropping
    event.dataTransfer.dropEffect = "move";

    if (draggedElement !== this) {
      // Determine insertion position based on mouse position
      const rect = this.getBoundingClientRect();
      const midpoint = rect.top + rect.height / 2;

      if (event.clientY < midpoint) {
        this.parentNode.insertBefore(draggedElement, this);
      } else {
        this.parentNode.insertBefore(draggedElement, this.nextSibling);
      }
    }
  });
});

Keyboard Navigation

javascript
class KeyboardNavigator {
  constructor(items) {
    this.items = Array.from(items);
    this.currentIndex = 0;

    this.init();
  }

  init() {
    document.addEventListener("keydown", this.handleKeyDown.bind(this));
  }

  handleKeyDown(event) {
    switch (event.key) {
      case "ArrowDown":
      case "j":
        event.preventDefault();
        this.selectNext();
        break;

      case "ArrowUp":
      case "k":
        event.preventDefault();
        this.selectPrevious();
        break;

      case "Enter":
        event.preventDefault();
        this.activateCurrent();
        break;

      case "Home":
        event.preventDefault();
        this.selectFirst();
        break;

      case "End":
        event.preventDefault();
        this.selectLast();
        break;
    }
  }

  selectNext() {
    this.currentIndex = Math.min(this.currentIndex + 1, this.items.length - 1);
    this.updateSelection();
  }

  selectPrevious() {
    this.currentIndex = Math.max(this.currentIndex - 1, 0);
    this.updateSelection();
  }

  selectFirst() {
    this.currentIndex = 0;
    this.updateSelection();
  }

  selectLast() {
    this.currentIndex = this.items.length - 1;
    this.updateSelection();
  }

  updateSelection() {
    this.items.forEach((item, index) => {
      if (index === this.currentIndex) {
        item.classList.add("selected");
        item.scrollIntoView({ block: "nearest" });
      } else {
        item.classList.remove("selected");
      }
    });
  }

  activateCurrent() {
    const current = this.items[this.currentIndex];
    current.click();
  }
}

// Usage
const navigator = new KeyboardNavigator(document.querySelectorAll(".nav-item"));

Double-Click to Edit

javascript
const items = document.querySelectorAll(".editable-item");

items.forEach((item) => {
  let clickCount = 0;
  let clickTimer = null;

  item.addEventListener("click", function (event) {
    clickCount++;

    if (clickCount === 1) {
      clickTimer = setTimeout(() => {
        // Single click
        selectItem(this);
        clickCount = 0;
      }, 300);
    } else if (clickCount === 2) {
      // Double click
      clearTimeout(clickTimer);
      clickCount = 0;
      startEditing(this);
    }
  });
});

function startEditing(element) {
  const input = document.createElement("input");
  input.value = element.textContent;
  input.classList.add("edit-input");

  const finishEdit = () => {
    element.textContent = input.value;
    input.remove();
  };

  input.addEventListener("blur", finishEdit);
  input.addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      finishEdit();
    } else if (event.key === "Escape") {
      input.remove();
    }
  });

  element.textContent = "";
  element.appendChild(input);
  input.focus();
  input.select();
}

Summary

The event object is the most central part of the event system, containing all information about the event. Through this chapter, you should master:

  1. Universal Properties: type, target, currentTarget, timeStamp, etc.
  2. Mouse Events: Position coordinates, button states, modifier keys
  3. Keyboard Events: key, code, modifier keys, repeat
  4. Control Methods: preventDefault(), stopPropagation(), stopImmediatePropagation()
  5. Practical Applications: Custom menus, drag-and-drop, keyboard navigation, etc.