ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 모던 자바스크립트 Deep Dive - 이벤트 전파와 이벤트 위임 , preventDefault , stopPropagation
    JavaScript 2021. 9. 26. 01:12

    [ 이벤트 전파 ]

     

    DOM 트리 상에 존재하는 DOM 요소 노드에서 발생한 이벤트는 DOM 트리를 통해 전파된다.

    이벤트 캡처링 : 이벤트가 상위요소 -> 하위 요소 전파 ( 화면 캡처는 자기 자신 화면에서 크기가 줄어들 수 있으나 커질 수는 없다. )

    이벤트 버블링 : 이벤트가 하위요소 -> 상위 요소 전파 ( 거품은 커진다. )

     

    // html
    ...
    <ul id = "fruits">
        <li id = "apple">apple</li>
        <li id = "banana">banana</li>
        <li id = "orange">orange</li>
    </ul>

    위와 같은 html이 있다.

    <script>
        const $fruits = document.querySelector('#fruits');
        const $banana = document.querySelector('#banana');
        
        $banana.addEventListener('click', e => {
            console.log(e.eventPhase)
            console.log(e.target)
            console.log(e.currentTarget)
        })
        
        $fruits.addEventListener('click', e => {
            console.log(e.eventPhase)
            console.log(e.target)
            console.log(e.currentTarget)
        },true)
        
    </script>

     

    위 코드의 banana를 클릭했을 때의 실행 결과를 유추 해보자.

    2
    <li id = "banana">banana</li>
    <li id = "banana">banana</li>
    
    3
    <li id = "banana">banana</li>
    <ul id = "fruits">...</ul>

    banana를 먼저 event등록을 해주었으니 먼저 실행되겠지? 하면 아니다.

     

    결과는

    1
    <li id = "banana">banana</li>
    <ul id = "fruits">...</ul>
    
    2
    <li id = "banana">banana</li>
    <li id = "banana">banana</li>

    이다.

    $fruit의 addEventListener로 세 번째 인수에 true를 넣었기 때문에
    이벤트 캡처링을 허가해준 것이다.

    따라서, 이벤트 순서를 window에서부터 내려온 것이다.
    즉, window -> ... -> ul -> li 순서로 이벤트 핸들러가 호출되는 것이다.

     

    <script>
        const $fruits = document.querySelector('#fruits');
        const $banana = document.querySelector('#banana');
        
        $fruits.addEventListener('click', e => {
            console.log(e.eventPhase)
            console.log(e.target)
            console.log(e.currentTarget)
        })
        
        $banana.addEventListener('click', e => {
            console.log(e.eventPhase)
            console.log(e.target)
            console.log(e.currentTarget)
        })
        
        $fruits.addEventListener('click', e => {
            console.log(e.eventPhase)
            console.log(e.target)
            console.log(e.currentTarget)
        },true)
        
    </script>

     

    그렇다면, 위 코드에서 banana를 클릭했을 때 출력 결과를 예상할 수 있을 것이다.

     

    1
    <li id = "banana">banana</li>
    <ul id = "fruits">...</ul>
    
    2
    <li id = "banana">banana</li>
    <li id = "banana">banana</li>
    
    3
    <li id = "banana">banana</li>
    <ul id = "fruits">...</ul>

     

    즉, 이벤트는 window -> ... -> ul -> li -> ul -> ... -> window 순서로 전파가된다.
    여기서 캡처링을 true하냐 마냐의 선택도 중요하다.

     

    [ 이벤트 위임 ]

     

    ...
    <ul id = 'fruits'>
        <li id = "apple">apple</li>
        <li id = "banana">banana</li>
        <li id = "orange">orange</li>
    </ul>
    ...
    const $fruits = document.querySelector('#fruits');
    const $apple = document.querySelector('#apple');
    const $banana = document.querySelector('#banana');
    const $orange = document.querySelector('#orange');
    
    function ativate({target}){
        target.classList.toggle('active');
    }
    
    $apple.onclick = ativate;
    $banana.onclick = ativate;
    $orange.onclick = ativate;

    위와 같은 방식으로 모든 li에 ativate 이벤트를 넣어주었다.

    보기에는 아무런 문제가 없다.
    하지만, li 아이템이 100개 혹은 1000개 그 이상이라면?
    이것은 DOM 요소에 이벤트 핸들러를 등록하므로 성능 저하의 원인이며 유지보수에도 적합하지 않은 코드이다.

    const $fruits = document.querySelector('#fruits');
    
    function activate({target}){
        //target이 fruits의 li가 아니면 나간다.
        if(!target.matches('#fruits > li')) return;
        
        [...$fruits.children].forEach($fruit => {
            $fruit.classList.toggle('active',$fruit === target);
         })
     });

    위의 코드 처럼 상위 DOM 요소에 event를 처리해주면 하위 DOM 요소들 판별 코드를 작성만 해주면 문제 없이 간결하게 코드를 작성할 수 있다.

     

    [ preventDefault ]

    preventDefault 메서드는 DOM 요소의 기본 동작을 중단 시킨다.

    기본 동작 : a 요소를 클릭하면 href 어트리뷰트에 지정된 링크로 이동 , checkbox 또는 radio 요소 클릭 등등...

    [ stopPropagation ]

    이벤트 객체의 stopPropagation 메서드는 이벤트 전파를 중지 시킨다.
    stopPropagation 메서드를 사용한 DOM 요소 이후로 이벤트 전파가 중지된다.

    댓글

Designed by Tistory.