December 2, 2021
캡처링과 버블링을 활용하면 강력한 이벤트 핸들링 패턴인 이벤트 위임(event delegation) 을 구현할 수 있습니다.
이벤트 위임은 비슷한 방식으로 여러 요소를 다뤄야 할 때 사용됩니다. 이벤트 위임을 사용하면 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있습니다.
공통 조상에 할당한 핸들러에서 event.target
을 이용하면 실제 어디서 이벤트가 발생했는지 알 수 있습니다. 이를 이용해 이벤트를 핸들링합니다.
예제를 살펴봅시다. 다음은 고대 중국 철학에서 유래한 팔괘도(Ba-Gua diagram) 입니다.
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를 강조 함
}
이벤트 위임을 다른 식으로도 활용할 수 있습니다.
‘저장하기’, ‘불러오기’, ‘검색하기’ 등의 버튼이 있는 메뉴를 구현해야 한다고 가정해봅시다. 각 버튼의 기능과 관련된 메서드 save
, load
, search
가 있는 객체도 이미 구현한 상태입니다. 이럴 때 버튼과 메서드를 어떻게 연결할 수 있을까요?
핸들러는 속성값을 읽고 적절한 메서드를 실행할 겁니다. 아래 예시를 실행해봅시다.
<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>
(*)
로 표시한 줄의 this.onClick
은 this
에 바인딩했다는 점에 주의해 주세요. 이렇게 하지 않으면 this
는 Menu
객체가 아닌 DOM 요소(elem
)를 참조하게 됩니다. 이렇게 되면 this[action]
에서 원하는 것을 얻지 못합니다.
이런 식으로 이벤트 위임을 활용하면 어떤 이점이 있을까요? 아래와 같은 장점이 있습니다.
이벤트 위임은 상당히 멋진 패턴입니다. DOM 이벤트에 적용할 수 있는 아주 유용한 패턴이죠,