개발자 Saaad
[JavaScript] 이벤트 위임 (Event Delegation) 본문
Event Delegation
캡처링과 버블링을 활용하면 강력한 이벤트 핸들링 패턴인 이벤트 위임(event delegation) 을 구현 할 수 있습니다.
이벤트 위임은 비슷한 방식으로 여러 요소를 다뤄야 할 때 사용됩니다.
이벤트 위임을 사용하면 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도
여러 요소를 한꺼번에 다룰 수 있습니다.
즉, 이벤트 위임은 '하위 요소의 이벤트를 상위 요소에 위임하는 것' 입니다.
즉, 하위 요소의 이벤트를 상위에서 제어합니다.
예제로 살펴보기
다음은 고대 중국 철학에서 유래한 팔괘도(Ba-Gua diagram) 입니다.
HTML은 대략 다음과 같습니다.
<table>
<tr>
<th colspan="3"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>
</tr>
<tr>
<td class="nw"><strong>Northwest</strong><br>Metal<br>Silver<br>Elders</td>
<td class="n">...</td>
<td class="ne">...</td>
</tr>
<tr>...2 more lines of this kind...</tr>
<tr>...2 more lines of this kind...</tr>
</table>
지금 보는 표에는 9개의 칸이 있습니다. 하지만 칸이 99개이든 9,999개이든 상관없습니다.
지금 해야 할 작업은 <td>를 클릭했을 때, 그 칸을 강조하는 것입니다.
각 <td>마다 onclick 핸들러를 할당하는 대신, ‘모든 이벤트를 잡아내는’ 핸들러를 <table> 요소에 할당해 보겠습니다.
<table> 요소에 할당하게 될 핸들러는 event.target을 이용해 어떤 요소가 클릭 되었는지 감지하고, 해당 칸을 강조하게 됩니다.
코드는 아래와 같습니다.
let selectedTd;
table.onclick = function(event) {
let target = event.target; // 클릭이 어디서 발생했을까요?
if (target.tagName != 'TD') return; // TD에서 발생한 게 아니라면 아무 작업도 하지 않습니다,
highlight(target); // 강조 함
};
function highlight(td) {
if (selectedTd) { // 이미 강조되어있는 칸이 있다면 원상태로 바꿔줌
selectedTd.classList.remove('highlight');
}
selectedTd = td;
selectedTd.classList.add('highlight'); // 새로운 td를 강조 함
}
이렇게 코드를 작성하면 테이블 내 칸의 개수는 고민거리가 되지 않습니다. 강조기능을 유지하면서 <td>를 언제라도 넣고 뺄 수 있게 됩니다.
하지만 단점도 있습니다.
위와 같이 구현하면 클릭 이벤트가 <td>가 아닌 <td> 안에서 동작할 수 있습니다.
팔괘도의 HTML을 살펴봅시다. <td>안에 중첩 태그 <strong>이 있는 것을 확인할 수 있습니다.
<td>
<strong>Northwest</strong>
...
</td>
<strong>을 클릭하면 event.target에 <strong>에 해당하는 요소가 저장됩니다.
따라서 table.onclick 핸들러에서 event.target을 이용해 클릭 이벤트가 <td>안쪽에서 일어났는지 아닌지를 알아내야 합니다.
이러한 단점을 반영하여 기능을 향상한 코드는 다음과 같습니다.
table.onclick = function(event) {
let td = event.target.closest('td'); // (1)
if (!td) return; // (2)
if (!table.contains(td)) return; // (3)
highlight(td); // (4)
};
설명
- elem.closest(selector) 메서드는 elem의 상위 요소 중 selector와 일치하는 가장 근접한 조상 요소를 반환합니다.
위 코드에선 이벤트가 발생한 요소부터 시작해 위로 올라가며 가장 가까운 <td> 요소를 찾습니다. - event.target이 <td>안에 있지 않으면 그 즉시 null을 반환하므로 아무 작업도 일어나지 않습니다.
- 중첩 테이블이 있는 경우 event.target은 현재 테이블 바깥에 있는 <td>가 될 수도 있습니다. 이런 경우를 처리하기 위해
<td>가 팔괘도 안에 있는지를 확인합니다. - 이제 진짜 td를 강조해 줍니다.
결론
이벤트 위임은 상당히 멋진 패턴입니다. DOM 이벤트에 적용할 수 있는 아주 유용한 패턴이죠,
이벤트 위임은 유사한 요소에 동일한 핸들러를 적용할 때 주로 사용하지만 꼭 이런 경우에만 사용할 수 있는 것은 아닙니다.
이벤트 위임은 다음과 같은 알고리즘으로 동작합니다.
1. 컨테이너에 하나의 핸들러를 할당합니다.
2. 핸들러의 event.target 을 사용해 이벤트가 발생한 요소가 어디인지 알아냅니다.
3. 원하는 요소에서 이벤트가 발생했다고 확인되면 이벤트를 핸들링합니다.
장점
- 많은 핸들러를 할당하지 않아도 되기 때문에 초기화가 단순해지고 메모리가 절약됩니다.
- 요소를 추가하거나 제거할 때 해당 요소에 할당된 핸들러를 추가하거나 제거할 필요가 없기 때문에 코드가 짧아집니다.
- innerHTML이나 유사한 기능을 하는 스크립트로 요소 덩어리를 더하거나 뺄 수 있기 때문에 DOM 수정이 쉬워집니다.
단점
- 이벤트 위임을 사용하려면 이벤트가 반드시 버블링 되어야 합니다. 하지만 몇몇 이벤트는 버블링 되지 않습니다. 그리고 낮은 레벨에 할당한 핸들러엔 event.stopPropagation()를 쓸 수 없습니다.
- 컨테이너 수준에 할당된 핸들러가 응답할 필요가 있는 이벤트이든 아니든 상관없이 모든 하위 컨테이너에서 발생하는 이벤트에 응답해야 하므로 CPU 작업 부하가 늘어날 수 있습니다. 그런데 이런 부하는 무시할만한 수준이므로 실제로는 잘 고려하지 않습니다.
하나 배운점
HTML의 tagName 속성은 항상 대문자로 반환된다! 따라서 태그이름을 비교할 때는 대소문자를 구분하지 않고 동작한다!
'학습 > kakao X goorm 풀스택12회차' 카테고리의 다른 글
[JavaScript] call, bind, apply (0) | 2024.11.28 |
---|---|
[JavaScript] this 키워드 (0) | 2024.11.27 |
[JavaScript] 이벤트 캡처링 (Event Capturing) (0) | 2024.11.27 |
[JavaScript] 이벤트 버블링 (Event Bubbling) (0) | 2024.11.27 |
[javascript] Event Listener & Event 객체 (0) | 2024.11.27 |