[JS] 이벤트 버블링과 이벤트 캡쳐링

Event Bubbling / Event Capturing

버블링과 캡쳐링을 본격적으로 알아보기 전에 간단한 코드를 한 번 살펴보자.

1
2
3
4
<div onclick="alert('The DIV handler!')">
DIV 태그
<p>P 태그</p>
</div>

See the Pen Ryewge by SoyeonJung (@je_ss2) on CodePen.

div태그에 alert('The DIV handler!')라는 이벤트 핸들러를 할당하였고, p태그에는 어떠한 핸들러도 할당하지 않았다. 그런데 p태그를 클릭해도 div태그에 할당된 핸들러가 실행이 된다.

조금 이상하다.
p태그를 클릭했는데 왜 div태그에 할당된 핸들러가 실행이 되는 것일까?


버블링 때문이다.

자바스크립트의 이벤트 버블링 때문에 위의 코드에서 div태그 안에 있는 p태그를 클릭하면 div태그에 할당된 클릭 이벤트가 발생하는 것이다.

그럼, 버블링이 정확히 뭔데?

이벤트 버블링의 원리는 간단하다.

“엘리먼트에 이벤트가 발생하면 먼저 해당 엘리먼트의 핸들러를 실행시킨 후, 그의 부모 엘리먼트의 핸들러를 실행시키고, 또 그 위의 조상 엘리먼트들의 핸들러를 실행시킨다”

마치 버블이 아래에서 위로 올라가는 것과 같아서 버블링이라고 한다. 아래의 예시를 보자.

1
2
3
4
5
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>

See the Pen WJaNgQ by SoyeonJung (@je_ss2) on CodePen.

form태그에는 alert('form')클릭 이벤트를 줬고, div태그에는 alert('div')클릭 이벤트를 줬고, p태그에는 alert('p')클릭 이벤트를 줬다. 그런데, p태그는 div태그가 감싸고 있고, 또 그 div태그는 form태그가 감싸고 있다.

따라서 이벤트 버블링으로 인해, p태그를 클릭하면 p -> div -> form순서로 알림창이 뜬다. 그리고 div태그를 클릭하면 div -> form순서로 알림창이 뜬다.

p태그를 클릭하면 window에서부터 p태그까지 요소 중간에 있는 이벤트들을 전부 검색해서 따로 메모리에 가지고 있다고 생각하면 된다. 해당 엘리먼트부터 차례대로 위로 올라가면서 이벤트들을 발생시키는 것, 그것이 바로 이벤트 버블링이다.


버블링 멈추기

기본적으로 이벤트 버블링은 항상 발생한다. 아래의 코드를 보자.

1
2
3
4
5
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p>P</p>
</div>
</form>

See the Pen WJabwo by SoyeonJung (@je_ss2) on CodePen.

form태그와 div태그에만 이벤트 핸들러를 주고, p태그에는 이벤트 핸들러를 주지 않았다. 하지만, 이벤트 버블링으로 인해 p 태그를 클릭하면 div -> form순서로 알림창이 뜬다.

이벤트 버블링이 발생되지 않았으면 좋겠어

이벤트 버블링은 event.stopPropagation()을 이용해서 간단하게 멈출 수 있다. 아래의 코드를 보자.

1
2
3
4
5
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="event.stopPropagation()">P</p>
</div>
</form>

See the Pen xjyGGK by SoyeonJung (@je_ss2) on CodePen.

p태그에 onclick="event.stopPropagation()"를 주었더니, p태그를 클릭해도 아까처럼 이벤트가 발생하지 않는다. 이벤트 버블링이 작동하지 않기 때문이다.


이벤트 캡쳐링

그렇다면 이번에는 이벤트 캡쳐링에 대해서 알아보자. 이벤트 캡쳐링도 이벤트 버블링과 비슷하다. 버블링이 아래에서 위로 올라가는 것이라면, 캡쳐링은 위에서 아래로 내려가는 것이다.

addEventListener를 이용하여 버블링과 캡쳐링에 대해서 살펴보자.

capturing단계에서 이벤트를 catch하려면 addEventListener의 세 번째 요소를 true로 설정해줘야 한다. 기본적으로 addEventListener의 세 번째 요소를 작성하지 않으면 default값이 false인데, false로 설정할 경우, 핸들러는 버블링 단계로 설정된다. 반면에 true로 설정할 경우, 핸들러는 캡쳐링 단계로 설정된다.

먼저 addEventListener의 세 번째 요소를 작성하지 않았을 경우를 살펴보자.

1
2
3
4
5
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
1
2
3
4
//JavaScript Code
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(elem.tagName));
}

See the Pen mLzywJ by SoyeonJung (@je_ss2) on CodePen.

p태그를 클릭하면 P -> DIV -> FORM -> BODY -> HTML순서로 알림창이 열린다. addEventListener의 세 번째 요소를 작성하지 않아서 기본값인 false로 되었기 때문에 핸들러가 버블링 단계로 설정되었기 때문이다.

이번에는 addEventListener의 세 번째 요소를 true로 작성해보자.

1
2
3
4
5
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
1
2
3
4
//JavaScript Code
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(elem.tagName), true);
}

See the Pen WJabBd by SoyeonJung (@je_ss2) on CodePen.

p태그를 클릭하면 HTML -> BODY -> FORM -> DIV -> P순서로 알림창이 열린다. addEventListener의 세 번째 요소를 true로 작성했기 때문에 핸들러가 캡쳐링 단계로 설정되었기 때문이다.

Share