설명에 앞서 코드를 먼저 살펴보자.
<div onclick="alert('The DIV handler!')">
DIV 태그
<p>P 태그</p>
</div>
div > p 형식의 코드로 부모인 div 에게 onclick 메소드가 할당되었다.
하지만 그 안의 p 태그를 클릭하더라도 div 태그의 onclick 메소드가 실행되는 것을 알 수 있다.
이러한 현상을 바로 이벤트 버블링 이라고 한다.
이벤트 버블링은 엘리먼트에 이벤트가 발생하면 먼저 해당 엘리먼트의 핸들러를 실행시킨 후, 그의 부모 엘리먼트의 핸들러를 실행시키고, 또 그 위의 조상 엘리먼트들의 핸들러를 차례로 실행시킨다
이제 다음의 코드를 보고 p 태그를 누르면 어떤 현상이 일어날지 감이 올 것이다.
<form onclick="alert('form')">
I'M Form
<div onclick="alert('div')">
I'm Div
<p onclick="alert('p')">I'm P</p>
</div>
</form>
맞다. p 알람 > div 알람 > form 알람 이 차례대로 울리게 된다.
만약 div 태그를 눌렀다면 div > form 순으로 알람이 울리게 된다.
그렇다면 왜 하나의 태그를 클릭해도 여러 개의 이벤트가 발생하는 것인가?
그 이유는 바로 "브라우저가 이벤트를 감지하는 방식" 때문이다.
브라우저는 특정 화면 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 화면 요소까지 이벤트를 전파시킨다.
이벤트 캡쳐링은 이벤트 버블링의 반대다.
버블링이 아래에서 위로 올라가는 방식이었다면 캡쳐링은 위에서 아래로 내려가는 방식이다.
이벤트 캡쳐링의 이벤트를 catch 하려면 addEventListner
의 세번째 인자를 true 로 설정해야 한다.
기본적으로 addEventListener의 세 번째 요소를 작성하지 않으면 default 값이 false인데, false로 설정할 경우, 핸들러는 버블링 단계로 설정된다.
<form onclick="alert('form')">
I'M Form
<div onclick="alert('div')">
I'm Div
<p onclick="alert('p')">I'm P</p>
</div>
</form>
for (let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(elem.tagName));
}
위 코드에서 p 태그를 클릭하면 P > DIV > FORM > BODY > HTML 순으로 이벤트 버블링이 일어난다.
이제 addEventLister
의 세번째 인자에 true 값을 넣어보자.
for (let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(elem.tagName), true);
}
이제는 p 태그를 클릭하면 HTML > BODY > FORM > DIV > P 순서로 이벤트 캡쳐링이 일어난다.
이벤트 버블링은 기본적으로 일어나는 현상이지만, 이를 강제로 멈추는 방법이 있다.
event.stopPropagation()
코드로 가능하다.
<form onclick="alert('form')">
FORM
<div onclick="alert('div')">
DIV
<p onclick="event.stopPropagation()">P</p>
</div>
</form>
다음과 같이 p 태그에 event.stopPropagation()
를 주게 되면 더이상 버블링이 일어나지 않게 된다.