캡처링과 버블링을 활용하면 이벤트 핸들링 패턴인 이벤트 위임을 구현할 수 있다.
이벤트 위임은 비슷한 방식으로 여러 요소를 다뤄야 할 때 사용된다. 이벤트 위임을 사용하면 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있다.
공통 조상에 할당한 핸들러에서 event.target
을 이용하면 실제 어디서 이벤트가 발생했는지 알 수 있다.
<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>
지금 해야 할 작업은 <td>
를 클릭했을 때, 그 칸을 강조하는 것
각 <td>
마다 onclick
핸들러를 할당하는 대신, '모든 이벤트를 잡아내는' 핸들러를 <table>
요소에 할당한다.
<table>
요소에 할당하게 될 핸들러는 event.target
을 이용해 어떤 요소가 클릭 되었는지 감지하고, 해당 칸을 강조하게 된다.
<script>
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를 강조 함
}
</script>
'저장하기', '불러오기', '검색하기'등의 버튼이 있는 메뉴를 구현해야 한다고 가정해보자. 각 버튼의 기능과 관련된 메서드 save, load, search가 있는 객체도 이미 구현한 상태이다.
메뉴 전체에 핸들러를 하나 추가해주고, 각 버튼의 data-action
속성에 호출할 메서드를 할당해 주는 방법이다.
<button data-action="save">저장하기</button>
핸들러는 속성값을 읽고 적절한 메서드를 실행한다.
<div id="menu">
<button data-action="save">저장하기</button>
<button data-action="load">불러오기</button>
<button data-action="search">검색하기</button>
</div>
<script>
class Menu {
constructor(elem) {
this._elem = elem;
elem.onclick = this.onClick.bind(this); // (*)
}
save() {
alert('저장하기');
}
load() {
alert('불러오기');
}
search() {
alert('검색하기');
}
onClick(event) {
let action = event.target.dataset.action;
if (action) {
this[action]();
}
};
}
new Menu(menu);
</script>
이런 식으로 이벤트 위임을 활용했을 때의 장점
.action-save
, .action-load
같은 클래스를 사용할 수도 있지만, data-action
속성이 좀 더 의미론적으로 낫고 CSS 규칙을 적용할 수도 있게 된다.