이벤트 캡처링과 버블링을 활용하여 이벤트 핸들링 패턴 이벤트 위임을 구현할 수 있습니다.
이벤트 위임은 실제 바닐라 JS로 웹 앱을 구현할 때 자주 사용하는 코딩 패턴입니다.
하위 요소에 각각 핸들러를 할당하지 않고, 상위 요소에서 이벤트를 제어하는 방식입니다.
공통 상위 요소에서 event.target
을 사용하면, 실제 이벤트가 어디서 발생했는지 알 수 있습니다.
table 태그에서 td요소에 대해 핸들러를 할당할 때, td요소 각각에 할당하는 것 대신 table태그에 붙힌 뒤,
event.target
을 통해 이벤트가 발생한 곳을 감지할 수 있습니다.
function onClick() {
const {target:{tagName}} = e
if(tagName != 'TD') return; // td가 아니라면 작업을 하지 않음
working(target);
}
table.addEventListner('click',onClick);
이렇게 하면, td 내부의 태그를 클릭할 때, 작업을 하지 않게됩니다.
closest
를 활용하여 기능을 향상할 수 있습니다.
function onClick() {
const td = e.target.closest('td');
if(!td) return;
if(!table.contains(td)) return // table 안의 td인지 확인합니다.
working(td);
}
각각 기능을 하는 버튼이 있는 메뉴를 만들어야 합니다.
버튼에 핸들러로 메서드를 할당하려면, 먼저 각각 핸들러를 할당하는 방법이 떠오릅니다.
하지만 이벤트 위임을 사용하면 조금 더 예쁘게 해결할 수 있습니다.
메뉴 전체에 핸들러를 추가하고, 각 버튼에 data-action
속성에 메서드명을 할당합니다.
HTML요소를 복잡한 객체로 만드는 법 data-* : MDN
<div id='menu'>
<button data-action='save'>저장하기</button>
<button data-action='load'>불러오기</button>
<button data-action='search'>검색하기</button>
</div>
class Menu {
constructor(elem) {
this._elem = elem;
elem.addEventListener('click',this.onClick);
}
save() {
console.log('save');
}
load() {
console.log('load');
}
search() {
console.log('search');
}
// menu 전체에 onclick 이벤트 위임 : 내부 요소 이벤트도 관리
// 클릭 요소의 action값 있으면 action명 method 실행
onClick = event=> {
const {action} = event.target.dataset;
if (action) {
this[action]();
}
}
}
new Menu(menu);
이벤트 위임을 통해 버튼마다 핸들러를 할당하지 않아도 되고, HTML에 메서드를 쓰면 됩니다.
언제든 버튼을 추가/제거 할 수 있어 HTML 구조가 유연해집니다.
이벤트 위임은 선언적 방식으로 행동을 추가할 때 사용할 수도 있습니다.
'숫자가 증가하는' 속성 data-counter
를 추가합니다.
<input type=button value=1 data-counter>
document.addEventListener('click',event=> {
const {target} = event;
if(target.dataset.counter != undefined) {
target.value++;
}
})
문서레벨의 핸들러를 할당할 때는 항상 addEventListener를 사용합니다.
on<evnet>
덮어 쓰기 때문에, 충돌의 가능성이 생깁니다.
집중할 것은 버튼이 아니라, 접근방식입니다.
data-counter
속성은 얼마든지 만들 수 있습니다. 필요시에 추가할 수 있습니다.
이벤트 위임을 사용해 새로운 행동을 선언하는 속성을 추가하여 HTML을 확장하였습니다.
이벤트 위임을 사용하여, js를 통해 따로 버튼에 핸들러를 할당하지 않아도
HTML 속성만 추가하여, 토글러를 추가할 수 있습니다.
<button data-toggle-id="subscribe-mail">Show subscrib-mail</button>
<form id="subscribe-mail" hidden>
mail : <input placeholder="Email..."/>
</form>
function toggle(event) {
const {toggleId} = event.target.dataset;
if (!toggleId) return;
const elem = document.querySelector(`#${toggleId}`);
elem.hidden = !elem.hidden;
}
document.addEventListener('click',toggle)
data-toggle-id
가 있는 요소를 클릭하면, 속성인 id
값을 구해, 해당 엘레먼트를 찾고,
그 요소를 토글합니다.
요소전체에 js로 기능을 구현하지 않고, 행동만 선언하면 되기 때문에 매우 편리합니다.
문서 레벨의 적절한 핸들러를 할당하면 페이지 내의 모든 요소에 행동을 쉽게 적용할 수 있습니다.
장점
많은 핸들러를 할당하지 않기 때문에, 초기화가 단순하고 메모리가 절약됩니다.
요소를 추가 제거할 때, 핸들러를 제거하지 않기 때문에 코드가 짧아집니다.
단점
위임된 이벤트가 잘 처리될 수 있게 버블링을 막으면 안됩니다. (stopPropagation())
모든 하위레벨 이벤트에 반응하므로 cpu 부하가 늘어나지만 무시할 수준입니다.