🖱️
DOM
중급 · 선수: 18_DOM_조작
19. 이벤트 처리
같은 함수 참조를 넘겨야 제거가 가능합니다. 익명 함수는 제거할 수 없습니다.
이벤트click버블링위임
소요 시간
⏱ 약 1시간
난이도
📊 중급
선수 조건
🎯 단원 18
결과물
같은 함수 참조를 넘겨야 제거가 가능합니다. 익명 함수는 제거할 수 없습니다.
이 강의에서 배우는 것
- 1`addEventListener` 로 이벤트 핸들러를 등록/해제한다.
- 2이벤트 객체에서 target, type 등을 읽는다.
- 3`preventDefault` 와 `stopPropagation` 을 구분한다.
- 4이벤트 위임(delegation) 을 활용해 많은 핸들러를 줄인다.
- 5캡처/버블 단계의 개념을 안다.
소개
같은 함수 참조를 넘겨야 제거가 가능합니다. 익명 함수는 제거할 수 없습니다.
핵심 개념
1. addEventListener
javascript
btn.addEventListener("click", handler);
btn.removeEventListener("click", handler);같은 함수 참조를 넘겨야 제거가 가능합니다. 익명 함수는 제거할 수 없습니다.
2. 이벤트 객체
콜백은 `Event` 객체를 받습니다.
- `event.target`: 실제로 이벤트가 발생한 요소
- `event.currentTarget`: 리스너가 붙은 요소
- `event.type`: "click", "input" 등
- `event.key`, `event.code`: 키보드 이벤트
3. preventDefault / stopPropagation
- `preventDefault()`: 기본 동작(폼 제출, 링크 이동 등)을 막음.
- `stopPropagation()`: 이벤트가 부모로 전파되는 것을 막음.
4. 이벤트 위임
자식이 많을 때 각각 등록하지 말고 부모 한 곳에서 처리합니다.
javascript
list.addEventListener("click", (e) => {
const li = e.target.closest("li");
if (!li) return;
console.log("클릭된 항목:", li.textContent);
});5. 캡처와 버블
이벤트는 window → 대상 → 부모로 전파됩니다(캡처 → 버블). 기본은 버블 단계에서 호출되며 `addEventListener(type, fn, { capture: true })` 로 캡처 단계 등록도 가능합니다.
핵심 예제
| 파일 | 다루는 내용 |
|---|---|
| index.html | 예제 HTML |
| 01_addeventlistener.js | 이벤트 등록/해제 |
| 02_event_object.js | 이벤트 객체 속성 |
| 03_prevent_default.js | preventDefault 사용 |
| 04_delegation.js | 이벤트 위임 |
src/01_addeventlistener.js
javascript
/**
* addEventListener / removeEventListener 사용.
*/
const btn = document.querySelector("#btn");
function onClick() {
console.log("버튼 클릭됨");
}
btn.addEventListener("click", onClick);
// 한 번만 실행되는 리스너
btn.addEventListener("click", () => console.log("한 번만!"), { once: true });
src/02_event_object.js
javascript
/**
* 이벤트 객체에서 정보를 읽습니다.
*/
const input = document.querySelector("#text");
input.addEventListener("input", (event) => {
console.log("type:", event.type);
console.log("target value:", event.target.value);
});
src/03_prevent_default.js
javascript
/**
* preventDefault 로 링크 이동을 막습니다.
*/
const link = document.querySelector("#link");
link.addEventListener("click", (event) => {
event.preventDefault();
console.log("링크 이동을 막았습니다.");
});
src/04_delegation.js
javascript
/**
* 부모 한 곳에서 자식 클릭을 처리(이벤트 위임).
*/
const menu = document.querySelector("#menu");
menu.addEventListener("click", (event) => {
const li = event.target.closest("li");
if (!li || !menu.contains(li)) return;
console.log("선택:", li.textContent);
});
src/index.html
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>이벤트 처리</title>
</head>
<body>
<button id="btn">클릭</button>
<a id="link" href="https://example.com">링크</a>
<ul id="menu">
<li>홈</li>
<li>소개</li>
<li>연락처</li>
</ul>
<input id="text" type="text" placeholder="입력" />
<script src="01_addeventlistener.js"></script>
<script src="02_event_object.js"></script>
<script src="03_prevent_default.js"></script>
<script src="04_delegation.js"></script>
</body>
</html>
자주 하는 실수
- `onclick = fn` 으로 한 번에 한 핸들러만 등록 가능한 문제.
- 익명 함수를 등록 후 제거 시도.
- `event.target` 과 `event.currentTarget` 혼동.
- 폼의 submit 에서 `preventDefault` 잊어 페이지가 새로고침.
- 동적으로 추가된 자식에 이벤트가 없어 위임 필요한 상황을 못 봄.
FAQ
Q1. once 옵션은?
`{ once: true }` 로 한 번만 실행 후 자동 해제됩니다.
Q2. passive 리스너는?
스크롤 성능을 위해 `{ passive: true }` 로 등록하면 preventDefault 호출이 무시됩니다.
Q3. 이벤트 전파를 막는 건 항상 좋나요?
다른 핸들러를 깨뜨릴 수 있으므로 꼭 필요한 경우만.
과제
목록에서 어떤 항목을 클릭해도 클릭된 텍스트를 출력하는 위임형 핸들러를 만드세요.
homework/README.md
목록(`<ul>`)에 항목 5개를 두고, 위임 한 방식으로 클릭된 항목의 텍스트를 콘솔에 출력하세요.
답안은 `answer/homework_01.html`, `answer/homework_01.js` 참고.
homework/answer/homework_01.html
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>과제 19-1</title>
</head>
<body>
<ul id="list">
<li>알파</li>
<li>베타</li>
<li>감마</li>
<li>델타</li>
<li>엡실론</li>
</ul>
<script src="homework_01.js"></script>
</body>
</html>
homework/answer/homework_01.js
javascript
/**
* 위임으로 목록 클릭을 한 곳에서 처리합니다.
*/
const list = document.querySelector("#list");
list.addEventListener("click", (event) => {
const li = event.target.closest("li");
if (!li || !list.contains(li)) return;
console.log("클릭:", li.textContent);
});
다음 단원
[20_폼과_입력](../20_폼과_입력/) — 폼 제어와 검증