October 20, 2021
캡처링
과 버블링
모두 이벤트 전파 단계 중 하나이다.
캡처링
은 이벤트가 상위 요소에서 하위 요소 방향으로 전파되는 단계이고, 버블링
은 이벤트가 하위 요소에서 상위 요소 방향으로 전파되는 단계이다.
브라우저는 처리해야 할 특정 사건이 발생하면 이를 감지하여 이벤트를 발생시킨다. (클릭, 키보드 입력, 마우스 이동 등)
이벤트가 발생했을 때 호출될 함수를 이벤트 핸들러라 하고, 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것을 이벤트 핸들러 등록이라고 한다.
개발자는 사용자가 버튼을 언제 클릭할지 알 수 없다. 즉, 함수를 언제 호출해야 하는지를 알 수 없다. 그러나 브라우저는 사용자의 버튼 클릭을 감지할 수 있다. 즉, 함수를 언제 호출해야 하는지를 알 수 있다. 그렇기 때문에 브라우저에게 이벤트 핸들러의 호출을 위임하는 것이다.
이벤트 핸들러를 등록하는 방식에는 이벤트 핸들러 어트리뷰트 방식, 이벤트 핸들러 프로퍼티 방식, addEventListener 방식, 총 3가지가 있다. (addEventListener 방식은 여러 개의 이벤트 핸들러를 등록할 수 있고, 다른 방식은 단 하나의 이벤트 핸들러만 등록할 수 있다.)
이벤트가 발생했을 때 생성된 이벤트 객체는 이벤트를 발생시킨 DOM 요소를 중심으로 DOM 트리를 통해 전파된다. 이벤트 전파는 3단계로 구분할 수 있다.
출처: 모던 자바스크립트 Deep Dive
캡처링 단계
: 이벤트가 상위 요소에서 하위 요소 방향으로 전파타깃 단계
: 이벤트가 이벤트 타깃에 도달버블링 단계
: 이벤트가 하위 요소에서 상위 요소 방향으로 전파캡처링과 버블링의 방향이 헷갈린다면, 비눗방울을 불고 비눗방울이 위로 올라가는 장면을 상상하면 기억하기 쉽다.
이벤트 전파를 이해함에 있어서 이 문구를 기억하고 있으면 도움이 된다.
조상에게 일어난 일을 자손은 알 수 없지만, 자손에게 일어난 일을 조상은 알 수 있다.
즉, 내 자손(Target)에게 일어난 일을 나(currentTarget)는 알 수 있다. (자손보다 먼저 아는 것이 캡처링, 나중에 아는 것이 버블링 - 기본적으로는 버블링으로 감지한다.)
어찌 보면 당연한 말이다. HTML 구조상 내 자손은 내 안에 있으므로 내 자손은 곧 나이기 때문이다.
(일이 일어났다는 것은 클릭 등의 이벤트가 그 요소에 의해 발생했다는 것을 의미한다.)
위 그림에서 li 요소를 클릭하면 클릭 이벤트가 발생하여 클릭 이벤트 객체가 생성되고 클릭된 li 요소가 이벤트 타깃이 된다. 이때 클릭 이벤트 객체는 window에서 시작해서 이벤트 타깃 방향으로 전파된다. (캡처링 단계)
이후 이벤트 객체는 이벤트를 발생시킨 이벤트 타깃에 도달한다. (타깃 단계)
이후 이벤트 객체는 이벤트 타깃에서 시작해서 window 방향으로 전파된다. (버블링 단계)
자바스크립트의 이벤트 핸들러 등록 및 처리는 기본적으로 버블링 과정을 통해 전파되기 때문에 캡처링 과정으로 이벤트를 전파하고 싶다면 addEventListener의 3번째 인자로 true를 전달해야 한다. (이벤트 핸들러 어트리뷰트/프로퍼티 방식으로 등록한 이벤트 핸들러는 캡처링 단계의 이벤트를 캐치할 수 없다.)
addEventListener 방식은 여러 개의 이벤트 핸들러를 등록할 수 있고, 캡처링 단계의 이벤트를 캐치할 수도 있으므로 다른 방식보다 addEventListener 방식을 사용하는 것을 권장한다. (React에서는 이벤트 핸들러 어트리뷰트 방식으로 이벤트를 처리한다.)
이벤트 전파를 이해하는 것은 매우 중요하다. 이벤트 전파를 잘 이해하고 있어야 자주 쓰이는 모달 같은 것도 구현할 수 있다.
유념해야 할 것이 있다.
캡처링 단계의 이벤트를 캐치해야 하는 경우는 거의 없다.
기본적으로 이벤트는 버블링을 통해 전파된다.(버블링 단계에서 이벤트를 감지한다.) 따로 설정을 해줘가며 캡처링을 해야 하는 경우는 거의 없다.
event.target
은 이벤트를 발생시킨 요소(나)이고, event.currentTarget
은 이벤트 핸들러가 바인딩된 요소(나 또는 조상)이다.
이벤트 위임
은 여러 개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 방법을 말한다. (위에서 말했듯이 이벤트 전파를 통해 내 자손에게 일어난 일을 나는 알 수 있기 때문에 가능하다.)
이벤트 위임을 하는 이유는 많은 하위 요소에서 일어난 이벤트에 반응해야 하는데 많은 요소들에 이벤트 핸들러를 등록하는 것은 성능 저하와 유지 보수성의 저하를 불러오기 때문이다.
이벤트 객체의 preventDefault
메서드는 DOM 요소의 기본 동작(a 요소 클릭 시 href 어트리뷰트에 지정된 링크로 이동, checkbox 혹은 radio 요소 클릭 시 체크 또는 해제 등)을 중단시킨다.
이벤트 객체의 stopPropagation
메서드를 이용하여 이벤트 전파를 방지한다. (내 조상이 내가 한 일을 모르게 하고 싶을 때 사용한다.)
그러나 또 다시 생각해봐야 하는 것이 있다.
이벤트 전파(버블링)를 막아야 하는 경우는 거의 없다.
버블링을 막아야 해결되는 문제라면 커스텀 이벤트 등을 사용해 문제를 해결할 수 있다. (커스텀 이벤트 객체는 버블링되지 않는다.)
40-28 ~ 40-36 코드를 실행해 보면 이해하는 데에 도움이 된다.