[JS] Event 위임

Yuno·2021년 7월 16일
1

모던JS

목록 보기
12/16
post-thumbnail

Event delegation (이벤트 위임)

이벤트 캡처링과 버블링을 활용하여 이벤트 핸들링 패턴 이벤트 위임을 구현할 수 있습니다.
이벤트 위임은 실제 바닐라 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 구조가 유연해집니다.

행동 패턴

이벤트 위임은 선언적 방식으로 행동을 추가할 때 사용할 수도 있습니다.

  1. 요소의 '행동을 설명'하는 커스텀 속성을 요소에 추가
  2. 문서 전체를 감지하는 핸들러가 이벤트를 추적합니다. 1에서 추가한 속성이 있는 요소에서 이벤트 발생시 작업을 수행합니다.

counter

'숫자가 증가하는' 속성 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을 확장하였습니다.

toggler

이벤트 위임을 사용하여, 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 부하가 늘어나지만 무시할 수준입니다.

profile
web frontend developer

0개의 댓글