아래 예시를 보자!
em 태그를 클릭해도 div에 할당한 핸들러가 동작한다!
🛁버블링의 원리
한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다!
가장 최상단의 조상요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당한된 핸들러가 동작한다!
다시한번 예시를 살펴보자
이 경우 가장 안쪽의 p 태그를 클릭하면 순서대로 다음과 같은 일이 벌어진다!
- p태그에 할당된 onclick 핸들러가 동작한다(1)
- 바깥의 div에 할당된 핸들러가 동작한다(2)
- 그 바깥의 form 태그에 할당된 핸들러가 동작한다(3)
이런 동작 방식 때문에 p 요소를 클릭하면 p -> div -> form 순서로 3개의 alert 창이 뜨게 된다!
이런 흐름을 '이벤트 버블링'이라고 부른다! 이벤트가 제일 깊은 곳에 있는 요소에서 시작해 부모 요소를 거슬러 올라가며 발생하는 모양이 마치 물속 거품과 닮았기 때문이다! 🧼
부모 요소의 핸들러는 이벤트가 정확히 어디서 발생했는지 등에 대한 자세한 정보를 얻을 수 있습니다.
⚡️이벤트가 발생한 가장 안쪽의 요소는 타겟(target) 요소라고 불리고, event.target을 사용해 접근할 수 있습니다.
🚨**event.target과 this(event.currentTarget)는 다음과 같은 차이점이 있습니다.
예시를 유심히 들여다 보자!
핸들러는 form.onclick 하나밖에 없지만 이 핸들러에서 폼 안의 모든 요소에 발생하는 클릭 이벤트를 '잡아내고(catch)'있습니다. 클릭 이벤트가 어디서 발생했든 상관없이 Form 요소까지 이벤트가 버블링되어 핸들러를 실행 시키기 때문이다!!
form.onclick 핸들러 내의 this 와 event.target은 다음과 같다.
이벤트 버블링은 타깃 이벤트에서 시작해서 html 요소를 거쳐 document 객체를 만날 때까지 각 노드에서 모두 발생한다.
그런데, 핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령할 수도 있습니다.
그럴 때는 이벤트 객체의 메서드인 event.stopPropagation()를 사용하자!
아래 예시에서 button을 클릭해도 body.onclick은 동작하지 않습니다.
🙋♂️event.stopPropagation()
한 요소의 특정 이벤트를 처리하는 핸들러가 다수인 상황에서, 핸들러 중 하나가 버블링을 멈추더라도 나머지 핸들러는 여전히 동작합니다.
event.stopPropagation()은 위쪽으로 일어나는 버블링은 막아주지만, 다른 핸들러들이 동작하는 건 막지 못합니다.
버블링을 멈추고, 요소에 할당된 다른핸들러의 동작도 막으러면 event.stopImmediatePropagation() 을 사용해야 합니다. 이 메소드를 사용하면 요소에 할당된 특정 이벤트를 처리하는 핸들러 모두가 동작하지 않을 수 있습니다.
이벤트엔 버블링 이외에도 '캡처링(capturing)'이라는 흐름이 존재한다!
실제 코드에서 자주 쓰이진 않지만, 종종 유용한 경우가 있음으로 알아보자!
이벤트 흐름에는 3단계가 있다!
아래 그림을 보고 테이블 안의 td를 클릭하면 어떻게 이벤트가 흐르는지 알아보자!
td를 클릭하면 (1)이벤트가 최상위 조상에서 시작해 아래로 전파(캡처링 단계)되고, (2)이벤트가 타깃 요소에 도착해 실행된 후(타깃 단계), (3)다시 위로 전파(버블링 단계)된다. 이런 과정을 통해 요소에 할당된 이벤트 핸들러가 호출된다.
아래 예시를 통해 전체 이벤트 발생에 대한 흐름을 확인해보자.
p를 클릭하면 다음과 같은 순서로 이벤트가 전달된다.
1. HTML -> BODY -> FORM -> DIV(캡처링 단계, 첫 번째 리스너)
2. P(타깃단계, 캡처링과 버블링 둘 다에 리스너를 설정했기 떄문에 두 번 호출됩니다.)
3. DIV -> FORM -> BODY -> HTML(버블링 단계, 두번째 리스너)
이벤트가 발생하면 이벤트가 발생한 가장 안쪽 요소가 '타깃 요소(event.target)'가 됩니다.
각 핸들러는 아래와 같은 event 객체의 프로퍼티에 접근할 수 있습니다.