이벤트 위임(Event Delegation)

2022.03.07 - [HTML, CSS] - (2022. 3. 7.) document.addEventListener()

 

위 글에서 document.addEventListener는 document 내부에 있는 모든 요소에 대해 반응함을 알 수 있음. document 이벤트가 내부의 <p>요소와 <input type="button">요소의 이벤트에 반응했던 이유는 이벤트 위임(Event Delegation) 때문.

 

 

1. 이벤트의 발생 단계

https://ko.javascript.info/bubbling-and-capturing

 

 

1. 캡처링 단계(Capture Phase): 이벤트가 DOM tree 하위 노드로 전파되는 단계

     ※ addEventListener의 capture 옵션 기본값이 false이므로 캡처링 단계에서는 이벤트 발생되지 않음

 

2. 타겟 단계(Target Phase): 이벤트가 실제 타겟 노드에 전달되는 단계   * 타겟 이벤트 리스너 실행됨 *

 

3. 버블링 단계(Bubbling Phase): 이벤트가 DOM tree 상위 노드로 전파되는 단계   

    ※ 타겟 요소의 모든 부모 요소에게 이벤트가 순차적으로 전파됨

 

<script>
    document.addEventListener('DOMContentLoaded',(event) => {
        const button1 = document.querySelector('input')
        const p1 = document.querySelector('p')

        document.addEventListener('click', (event) => {
            alert('본문을 클릭했어요.')
        })
        button1.addEventListener('click', (event) => {
            alert('버튼을 클릭했어요.')
        })
        p1.addEventListener('click', (event) => {
            alert('글자를 클릭했어요.')})
    })
 
</script>
<body>
    <p>클릭?</p>
    <input type="button" value="클릭">
</body>

위와 같은 코드를 실행해보면 각 요소에 대한 event listener를 어떤 식으로 배치하던간에 이벤트 버블링이 일어나기 때문에 하위 노드 이벤트 → 상위 노드 이벤트 순으로 발생함. 이때, 이벤트 캡처링/버블링이 발생하는 노드는 window → ... → 부모 노드 타겟 노드이고, 자식 노드, 형제 노드, 위계구조 바깥의 노드는 이벤트 캡처링/버블링과 관련이 없음에 주의! 

 

 

 

2.event.target 과 event.currentTarget

이벤트 발생 단계에 의해 event.target과 event.currentTarget가 가리키는 요소는 아래와 같음.

① event.target: 실제로 이벤트가 발생한 요소(타겟 요소)
② event.currentTarget이벤트 핸들러가 할당된 요소

 

예를 들어, <p> 요소(p1)에서 클릭 이벤트가 발생했을 때

p1.addEventListener('click', () => { /* */ });

① event.target: <p>
② event.currentTarget: <p>

document.addEventListener('click', () => { /* */ });

① event.target: <p>
② event.currentTarget: document

 

 

 

3. 이벤트 위임(Event Delegation)

이벤트 위임은 상위 요소에서 하위 요소의 이벤트를 제어하는 방식으로, ① 특정 요소의 여러 하위 요소의 이벤트를 비슷하게 다루는 경우, ② 동적으로 생성되는 하위 요소에 대한 이벤트를 다루는 경우에 유용하게 쓰일 수 있음

 

하위 노드에서 클릭 이벤트가 발생했을 때...

1. 캡처링(Capturing): 상위 노드에서 이벤트 발생 → 하위 노드에서 이벤트 발생

2. 버블링(Bubbling): 하위 노드에서 이벤트 발생  상위 노드에서 이벤트 발생

 

 

A. 이벤트 위임의 활용

1. 상위 요소에 대한 event handler로 이벤트가 발생한 하위 요소 선택하기

요소 thisParent 내부의 class가 thisClass인 요소 child에서 클릭 이벤트가 발생하면, 요소 child를 포함한 상위 요소 <label> 요소 otherParent로 이동시키는 함수

thisParent.addEventListener('click', (e) => {
        if ( e.target && e.target.matches('.thisClass') ) {
        
            const child = thisParent.querySelectorAll('.thisClass');
          
            if ( child ) {   // 노드 이동
                otherParent.appendChild(child.closest('label'));
                alert('다른 부모 요소로 이동!');
            }
        }
    })

thisParent 내부의 임의의 요소에서 클릭 이벤트가 발생하면 이벤트 타겟(e.target)이 내가 원하는 요소가 아닐 수 있으므로 matches() 메서드를 이용해서 검사

thisParent 내부의 class가 thisClass child를 찾아서

③ 조건에 부합하는 요소가 존재하면 childotherParent로 이동

 

※ child가 문서 로드 이후에 동적으로 생성되는 객체라도 해당 객체를 이동시킬 수 있음

 

 

B. 이벤트 위임과 관련된 유용한 메서드

1. Node.parentNode #

해당 Node의 바로 상위 요소를 반환

 

  ※ 객체 Element는 Node의 하위 객체이므로 Element에 대해서도 적용 가능함

element.parentNode   // 요소 element의 가장 인접한 부모 요소를 선택

 

2. Element.closest('selector') #

해당 Element부모 요소 중 selector와 일치하는 요소를 상위 방향으로 탐색하여 반환

element.closest('label')   // 요소 element의 부모 요소에서 <label>인 요소 선택

 

3. Element.matches('selector') #

해당 Element가 selector와 일치하는지 여부를 반환

// 1. 이벤트 타겟이 존재하고,
// 2. 해당 이벤트 타겟의 id가 'thisId'인 경우에만 if문 실행
if (e.target && e.target.matches('#thisId')) {
		console.log(e.target);
}

 

4. Node.contains(Node) #

해당 노드가 주어진 인자 Node를 자손으로 가지는지 여부를 반환(자기 자신일 경우에도 true 반환)

const container = document.querySelector("#container");

if (container.contains(e.target)) {
    /* */
}

 

 

 

 

 

 

[참고]

[JavaScript] 이벤트 위임

[JS] 이벤트 전파와 이벤트 위임

Javascript delegate for elements with child nodes

'JavaScript' 카테고리의 다른 글

this  (0) 2022.03.24
유사 배열(Array-like)  (0) 2022.03.24
Closure와 즉시실행함수(IIFE)  (0) 2022.03.12
변수의 선언(const, let, var)과 Scope  (0) 2022.03.09
event.currentTarget의 속성으로 무엇을 쓸 수 있나?  (0) 2022.03.07