--- id: wiki-2026-0508-events title: Events category: 10_Wiki/Topics status: verified canonical_id: self aliases: [DOM Events, Event Handling, JavaScript Events] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [frontend, dom, events, javascript] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: javascript framework: 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 ```js 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 ```js // 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) ```js // Tells browser handler won't preventDefault → no scroll-blocking window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('touchmove', onTouchMove, { passive: true }); ``` ### AbortController (modern cleanup) ```js 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 ```js form.addEventListener('submit', (e) => { e.preventDefault(); // cancel default (form GET/POST) e.stopPropagation(); // don't bubble to ancestors }); ``` ### Custom Events ```js 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 ```jsx 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 ; } ``` ### Pointer Events (drag) ```js 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 ```js 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 - 부모: [[DOM]] · [[JavaScript]] - 응용: [[Drag and Drop]] - Adjacent: [[Accessibility (A11y)|Accessibility]] · [[Performance]] ## 🤖 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 |