Files
2nd/10_Wiki/Topics/Frontend/Events.md
T
2026-05-10 22:08:15 +09:00

5.5 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-events Events 10_Wiki/Topics verified self
DOM Events
Event Handling
JavaScript Events
none A 0.9 applied
frontend
dom
events
javascript
2026-05-10 pending
language framework
javascript dom

Events

매 한 줄

"매 DOM event 는 capture → target → bubble 3-phase 의 propagation". Events 는 user/system action (click, input, scroll, ...) 을 JS handler 에 dispatch 하는 mechanism. 매 modern app 의 React SyntheticEvent / addEventListener / passive listener 의 mix 의 사용.

매 핵심

매 Event Phases

  • Capture phase: root → target (top-down). { capture: true } 의 trigger.
  • Target phase: target element 의 listener.
  • Bubble phase: target → root (default). e.stopPropagation() 의 중단.

매 Event Types

  • Mouse: click, dblclick, mousedown/up, mousemove, mouseenter/leave (no bubble), mouseover/out (bubble).
  • Keyboard: keydown, keyup (NOT keypress — deprecated).
  • Touch/Pointer: pointerdown/up/move (unified mouse+touch), touchstart/move/end.
  • Form: input (every keystroke), change (commit), submit, focus/blur (no bubble), focusin/out (bubble).
  • Lifecycle: DOMContentLoaded, load, beforeunload, visibilitychange.
  • Custom: new CustomEvent('foo', { detail: {...} }).

매 응용

  1. UI interaction (button click, form submit).
  2. Event delegation (single listener for many children).
  3. Drag-and-drop (pointer events).
  4. Keyboard shortcuts / accessibility.

💻 패턴

Basic addEventListener

button.addEventListener('click', (e) => {
  console.log('clicked', e.target);
});

// removeEventListener requires same fn reference
const handler = (e) => console.log(e);
el.addEventListener('click', handler);
el.removeEventListener('click', handler);

Event Delegation

// Single listener on parent — handles all child clicks
document.querySelector('#list').addEventListener('click', (e) => {
  const item = e.target.closest('[data-id]');
  if (!item) return;
  console.log('item:', item.dataset.id);
});

Passive Listener (Scroll Performance)

// Tells browser handler won't preventDefault → no scroll-blocking
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('touchmove', onTouchMove, { passive: true });

AbortController (modern cleanup)

const ctrl = new AbortController();
el.addEventListener('click', handler, { signal: ctrl.signal });
el.addEventListener('mouseover', other, { signal: ctrl.signal });
// Remove all at once
ctrl.abort();

Stop Propagation vs Prevent Default

form.addEventListener('submit', (e) => {
  e.preventDefault();   // cancel default (form GET/POST)
  e.stopPropagation();  // don't bubble to ancestors
});

Custom Events

const evt = new CustomEvent('user:login', {
  detail: { userId: 42 },
  bubbles: true,
});
element.dispatchEvent(evt);

document.addEventListener('user:login', (e) => {
  console.log(e.detail.userId);
});

React SyntheticEvent

function Button() {
  // React pools events; e.persist() no longer needed (React 17+)
  const onClick = (e) => {
    console.log(e.nativeEvent); // underlying DOM event
    console.log(e.currentTarget);
  };
  return <button onClick={onClick}>Click</button>;
}

Pointer Events (drag)

let dragging = false;
el.addEventListener('pointerdown', (e) => {
  el.setPointerCapture(e.pointerId);
  dragging = true;
});
el.addEventListener('pointermove', (e) => {
  if (!dragging) return;
  el.style.transform = `translate(${e.clientX}px, ${e.clientY}px)`;
});
el.addEventListener('pointerup', (e) => {
  el.releasePointerCapture(e.pointerId);
  dragging = false;
});

Debounce / Throttle

function debounce(fn, ms) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
}
input.addEventListener('input', debounce(search, 300));

매 결정 기준

상황 Approach
다수 child 의 listener Delegation (single parent listener)
Scroll/touch handler { passive: true }
다수 listener cleanup AbortController
Mouse + touch unified Pointer events
Cross-component coordination CustomEvent or state library

기본값: addEventListener + delegation + AbortController for cleanup.

🔗 Graph

🤖 LLM 활용

언제: event listener pattern 의 question, propagation 의 debug, delegation 의 implement. 언제 X: framework-specific event system 의 deep dive (React/Vue 의 own docs 의 참조).

안티패턴

  • Inline onclick="" attribute: HTML/JS 의 mix, CSP 의 violation.
  • No cleanup in SPA: memory leak. 매 unmount 의 removeEventListener 의 호출.
  • scroll without passive: 60fps scroll 의 block.
  • stopPropagation() overuse: delegation pattern 의 break.
  • Listener on every list item: N 의 listener 대신 delegation 의 사용.

🧪 검증 / 중복

  • Verified (MDN Web Docs — Event reference, WHATWG DOM spec).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — DOM event handling full content