본문 바로가기
Javascript

[Javascript] 이벤트 실행 순서, 캡처링, 버블링

by 오늘의개발부 2020. 11. 19.
반응형

 위와 같은 형식의 Html 구조가 잡혀있다.

 1과 2는 페이지에 그려진 div이고, 3은 button을 누르면 동적으로 생성되는 div이다. 모든 div는 누르면 alert를 띄우는 이벤트가 걸려있다. 그리고 동적으로 생성되는 div3은 동적 이벤트 바인딩을 아래처럼해줬다. 

$(document).on('click', '#div3', function(){
	alert(3);
});

div3을 클릭했을 때 내가 기대한 건 alert가 3, 2, 1 순서로 뜨는 것이었다.

하지만 실제로는 2, 1, 3 순서로 떴다.

 

이벤트 발생 순서는 depth가 가장 깊은 element의 이벤트부터라고 알고 있었는데 왜 겉에 있는 2번부터 뜨고 마지막에 3번이 뜨는 걸까?

 

이벤트에대해 제대로 알아보기 위해 구글링을 해봤다.

 

표준 Dom 이벤트에서 정의한 이벤트의 흐름은 다음과 같다.

 

1. 캡처링 단계 - 이벤트가 하위 요소로 전파된다.

2. 타겟 단계 - 이벤트가 실제 타깃 요소에 전달된다.

3. 버블링 단계 - 이벤트가 상위 요소로 전파된다.

 

아래 html을 참고하여 div2를 클릭했을 때 일어나는 이벤트의 흐름을 살펴보자.

<!DOCTYPE html>
<html lang="en">
<head>
 ...
</head>
<body>
    <div id="div1" style="width: 500px; height: 500px; background-color: aqua;">
        1
        <div id="div2" style="width: 300px;height: 300px;background-color: yellow;">
            2
        </div>
    </div>
    <input type="button" onclick="createDiv()" value="button">
</body>
</html>

1. 캡처링 단계 window → document → <html> → <body> → <div id="div1">

2. 타겟 단계 <div id="div1"> <div id="div2">

3. 버블링 단계 <div id="div2"> <div id="div1"> <body> <html> document window 

 

이벤트는 최상위의 window부터 시작해 하위요소로 전파되고, 타깃에 도착한 후 다시 상위 요소로 전파된다.

캡처링 단계는 이용해야하는 경우가 많지 않기 때문에 보통 사용하는 on<event> 프로퍼티나, addEventListner를 이용한 이벤트 할당은 캡처링 단계를 알 수 없고 다겟 단계나 버블링 단계에서 동작한다고 한다.

 

캡처링 단계의 이벤트를 잡아내려면 addEventListner의 세번째 인자를 {capture : true}, 혹은 true로 설정해줘야한다. default는 false이다. true면 캡처링 단계에서 동작, false면 버블링 단계에서 동작한다.

참고로 addEventListner한 이벤트를 removeEventListner로 제거할 때는 동작 단계가 같아야한다. 즉, addEventListner(..., true)였다면 removeEventListner(..., true)로 제거해줘야한다.

 

 

 

이쯤 알아보고 원래의 문제로 돌아가면, 왜 3, 2, 1이 아니라 2, 1, 3으로 alert가 뜬 것일까?

이벤트 바인딩을 document에 해줬기 때문이었다.

$(document).on('click', '#div3', function(){
	alert(3);
});

모든 이벤트 핸들러는 default로 버블링 단계에서 동작하고 있었고,

<div id="div2">  <div id="div1">  <body>  <html>  document 순으로 동작했기 때문에 버블링 단계에서 document까지 도달해야 핸들러가 동작하면서 alert(3)을 실행한 것이다.

 

 

document에 걸어줬던 이벤트를 div2에 걸어주는 방법으로 해결했다.

$('#div2').on('click', '#div3', function(){
	alert(3);
});

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
</head>
<body>
    <div id="div1" style="width: 500px; height: 500px; background-color: aqua;">
        1
        <div id="div2" style="width: 300px;height: 300px;background-color: yellow;">
            2
        </div>
    </div>
    <input type="button" onclick="createDiv()" value="button">
</body>
    <script>
        $('#div1').on('click', function(){
            alert(1);
        })

        $('#div2').on('click', function(){
            alert(2);
        })

        function createDiv(){
            $('#div2').append('<div id="div3" style="width: 100px;height: 100px;background-color: blue;">3</div>');
        }
		
        //document에 click 이벤트 바인딩하면 2, 1, 3으로 뜬다.
        $(document).on('click', '#div3', function(){
            alert(3);
        })
        
        //#div2에 click 이벤트 바인딩
        $('#div2').on('click', '#div3', function(){
            alert(3);
        })


    </script>
</html>

 

 

반응형

'Javascript' 카테고리의 다른 글

[ES6, ES2020] 유용한 자바스크립트 최신 문법  (2) 2021.08.04
스벨트 기본 기록  (0) 2021.06.22