브라우저에서 어떻게 특정 화면 요소의 이벤트를 감지하는지 그리고 그 이벤트를 어떻게 다른 화면 요소에 전파하는지 알아보았다.
이벤트 버블링은 특정 화면 요소에서 이벤트가 발생했을 때 해당 이벤트가 더 상위의 화면 요소들로 전달되어 가는 특성을 의미한다.
이벤트 캡쳐는 이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식이다.
모달을 구현하고, 모달 밖을 누르면 모달이 꺼지고, 모달 내부를 누르면 아무 반응도 없게 구현하고 싶었다. 하지만 분명 모달의 바깥을 눌러야 모달이 닫히는 함수를 만들었음에도 불구하고, 내부를 클릭했을 때도 같이 꺼지는 문제가 발생할 수 있다. 이것은 바로 이벤트 버블링때문이다.
(이미지 출처: https://www.howdy-mj.me/dom/event-capturing-and-bubbling/)
이벤트 캡쳐링은 클릭이벤트가 발생한 지점을 찾아 내려가는 것이고, 이벤트 버블링은 하위의 클릭 이벤트가 상위로 전달되는 것이다.
브라우저는 특정 화면 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 화면 요소까지 이벤트를 전파시킨다. 따라서, Green-> Pink-> Body 순서로 div 태그에 등록된 이벤트들이 실행된다다. 마찬가지로 pink div 태그를 클릭했다면 Pink -> Body 순으로 클릭 이벤트가 동작한다. 이러한 브라우저의 이벤트 감지 방식 때문에 모달창 안쪽을 클릭하여도 같이 꺼져버리는 것.
캡처링의 경우, addEventListener()의 세 번째 인자가 캡쳐링 여부를 결정한다.
target.addEventListener(type, listener[, useCapture]);
기본 값은 false로 되어있기 때문에 버블링만 발생하며 이를 true로 바꾸면 캡쳐링이 발생한다. 위의 이미지에서 보자면 초록색을 누르면 html > body > pink div > green div 순으로 console이 찍힌다.
const body = document.querySelector('body')
body.addEventListener(
'click',
function() {
console.log('clicked body')
},
true
)
이렇게 복잡한 이벤트 전달 방식을 막고, 그냥 원하는 화면 요소의 이벤트만 의도하고 싶을 때에는 event.stopPropagation()
이라는 api를 사용한다.
function logEvent(event) {
event.stopPropagation();
}
위 API는 해당 이벤트가 전파되는 것을 막는다. 따라서, 이벤트 버블링의 경우에는 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하는 것을 방해한다.
그리고 이벤트 캡쳐의 경우에는 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들로 이벤트를 전달하지 않는다.
(이벤트 버블링 : 클릭한 요소의 이벤트만 발생 / 이벤트 캡쳐링 : 최상위 요소만 동작)
아래 예시코드에서 pink div를 클릭 할 때에는 오직 ‘pink div’만 console에 찍히는 것을 확인할 수 있다. 이는 클릭한 element의 이벤트만 발생시키고 상위로 이벤트 전달하는 것을 막아준다. 캡쳐링에서 e.stopPropagation()을 걸 경우, 클릭한 element의 최상위 이벤트만 동작하고, 하위 이벤트는 발생하지 않는다.
html.addEventListener("click", function () {
console.log("html");
});
body.addEventListener("click", function () {
console.log("body");
});
pinkDiv.addEventListener("click", function (e) {
e.stopPropagation();
console.log("pink div");
});
greenDiv.addEventListener(
"click",
function (e) {
e.stopPropagation();
console.log("green div");
},
true
);