JS Sketchbooksee JavaScript think ✏️
← back to phase 7

7.4 — Events

Until now, your programs ran top to bottom and ended. Pages don't end — they wait for the human. The mechanism is events: the browser announces happenings — a click, a keystroke, a form submitted — and your code volunteers listeners: "when that happens on this element, run this function."

You already own every ingredient: listeners are callbacks (3.8), delivered through the event queue (6.2), receiving an object full of facts (4.4).

And as a future automation engineer, this is doubly yours: every Playwright action — click, fill, press — works by dispatching exactly these events. Today you learn what your tests will one day fire.

watch it happen
const btn = document.querySelector("#save");

btn.addEventListener("click", (event) => {
  console.log(event.type);
  console.log(event.target.id);
});

const box = document.querySelector("#name");

box.addEventListener("input", (event) => {
  console.log(event.target.value);
});

box.addEventListener("keydown", (event) => {
  if (event.key === "Enter") {
    console.log("submitted!");
  }
});

btn.addEventListener("click", handler) wires it: on THIS element, for THIS event name, run THIS callback. Nothing runs now — line 3 just registers, like setTimeout registered (6.1). The page keeps waiting; your function waits with it.

listeners wired to elements#save · clickaddEventListener = “when THIS happens on THIS element, run THIS function”(console: nothing yet)
under the hood

The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”

The event object is per-type specialized (5.6's inheritance, again): a click hands you a MouseEvent (with coordinates), a keydown a KeyboardEvent (with .key), a submit a SubmitEvent — all extending Event.

.type, .target, and 7.5's preventDefault live on the base. One family tree, one lesson — yours already.

Housekeeping that separates juniors from seniors: removeEventListener(name, fn) needs the SAME function reference — which is why handlers you plan to remove get names instead of inline arrows.

Forgotten listeners on long-lived pages are a classic 5.7 leak: the listener's rope keeps its closure reachable forever.

Your exercise builds the pattern's heart — a name → callbacks map — and it isn't a toy: Node's EventEmitter (Phase 9's world) IS this object, and the DOM's implementation is this plus a tree. Build it once, recognize it everywhere: that's the whole curriculum's bet.

your turn

⌨️ build addEventListener’s heart

Every event system — the DOM’s included — is a name → list-of-callbacks map. Build one in twenty lines and the browser’s version will never feel magic again.

requirements:

  • An object listeners (start empty). A function on(name, fn): if listeners[name] doesn't exist yet, create an empty array there (4.4's assignment-creates!); then push fn in.
  • A function emit(name, payload): loop the array at listeners[name] (if any) and call each callback with payload.
  • Register TWO listeners for "click" — one printing "heard: " + payload, one printing "also: " + payload — and one for "scroll" printing "scrolled". Emit "click" with "save-btn". Only the two click listeners may fire, in registration order.

when you press RUN, the console must show exactly:

heard: save-btn
also: save-btn

✏️ Quick check 1

Which property of the event object tells you the element that was actually hit? Type it (event.___):

✏️ Quick check 2

A user types "hi" (two keystrokes) into a box with an "input" listener. How many times does the listener fire? Type the number.

✏️ Quick check 3

Inside a keydown handler, which property holds WHICH key was pressed? Type it (event.___):

teach it back

🗣️ Now teach it back

Explain to a friend how a click becomes their function running: the registration, the event object, the queue underneath — and why a busy page makes clicks feel dead.

Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.

a few sentences, minimum — you’ve got this
to remember
addEventListener(name, fn) = register a callback for a happening on an element; many can stack, firing in order.
Handlers receive the EVENT OBJECT: .type, .target (live ref), plus specifics (.key, coordinates). Three of the big four here — click, input, keydown; submit joins them once we hit forms (7.6).
Delivery = 6.2’s queue (macrotasks) — blocked stacks make clicks feel dead. Playwright actions DISPATCH these exact events.