이전 프로젝트에서 버튼을 클릭하면 원치 않는 기능이 작동되었던 기억이 있다.그 때 일어났던 현상이 바로 '이벤트 버블링'이라는 것이었는데, 결국 해결은 하긴 했지만 시간이 지나니 뭐가 뭐였는지 기억이 흐릿해져갔다.
면접 질문으로도 자주 나온다하니
이벤트의 현상 대해 정리해보자~
이벤트 버블링이란, 상위 엘리먼트와 하위 엘리먼트가 있을때 하위 엘리먼트 이벤트를 클릭하면 클릭한 하위 엘리먼트에서부터 상위 엘리먼트까지 이벤트가 전달되는 방식을 말한다.
<D onClick={() => console.log('D')}> // 검정박스
<U onClick={() => console.log('U')}> // 초록박스
<L onClick={() => console.log('L')}>예시</L> // 빨간박스
</U>
</D>
여기서 가장 하위 엘리먼트인 <L>
을 클릭하게되면 어떻게 될까?
<L>
태그를 눌렀을 시, L-U-D
차례로 콘솔이 찍히는것을 볼 수 있다. 이게 바로 버블링이다
좀 더 버블링 예시를 보자
<U>
를 눌렀을 시, U-D
<D>
를 눌렀을 시, D
이벤트 버블링 동작
<L>
태그를 눌렀을 시,L-U-D
차례로 콘솔이 찍히는 부분이 잘린 것 같다. gif는 그냥 참고로 봐주세욤~
다음은 이벤트 캡쳐링에 대해 알아보자.
이벤트 캡쳐링이란, 상위, 중간, 하위 엘리먼트가 있을 때 (중간 엘리먼트에 캡쳐링 옵션을 준 상태), 하위 엘리먼트 이벤트를 클릭하면 그 위의 상위 엘리먼트들 중 캡쳐링이 있는 엘리먼트를 찾아 이벤트를 발생시키고, 그 다음 클릭한 하위 엘리먼트부터 상위 엘리먼트까지 이벤트가 전달되는 방식을 말한다.
예를 들어보자.
<C onClick={() => console.log('콘')}>
<D onClick={() => console.log('D')}>
<U onClickCapture={() => console.log('U')}>
<L onClick={() => console.log('L')}>예시</L>
</U>
</D>
</C>
이벤트 캡쳐링을 발생시키기 위해서는 옵션을 줘야 한다.
(이벤트는 기본적인 동작방식이'이벤트 버블링'이기 때문에 자바스크립트 기준addEventListener()
의
마지막 인자로{capture: true}
를 전달해주면 캡쳐링으로 바꿔줄 수 있다.)
나는 jsx로 예를 들었기 때문에onClickCapture
이벤트를 전달해 주었다.
하위 엘리먼트 을 누르면 어떻게 될까?
U-L-D-콘
하위엘리먼트 <L>
을 눌렀을 시, 상위 엘리먼트들 중 캡쳐링이 있는 엘리먼트를 찾아 먼저 이벤트 발생시키고(U
), 클릭했던 하위 엘리먼트(L
) ~ 상위 엘리먼트 까지(D-콘
) 이벤트가 버블링으로 실행되는걸 볼 수 있다.
이벤트 캡쳐링
만약 클릭한 상위 엘리먼트들 중 캡쳐링이 없다면 무난히 버블링으로 이벤트가 발생하는것 또한 gif를 통해 볼 수 있다.
버블링과, 캡쳐링을 이해했다면 이벤트 위임
을 이해하는건 쉽다
이벤트 위임이란, 상위 엘리먼트와 하위 엘리먼트들이 있을 때 하위 엘리먼트들에 각각 이벤트 핸들러를 달지 않고 상위 엘리먼트에 이벤트 핸들러를 달아 하위 엘리먼트들을 제어하는 방식을 말한다.
이번엔 자바스크립트 코드 예시다.
<ul onclick="alert("주의하세용")">
<li>첫번째</li>
<li>두번째</li>
<li>세번째</li>
</ul>
<li>
태그 어느것을 클릭해도 <ul>
의 이벤트가 발생하는걸 볼 수 있다.
이벤트 위임을 사용해서 장점은 무엇일까? 👍
- 코드가 깔끔해진다., 가독성이 좋다 (
onclick="alert("주의하세용")"
의 이벤트가<li>
각각의 태그에 달려있다 생각해보면 이벤트 위임을 사용해서 코드를 작성하는것이 얼마나 깔끔한지 알 수 있을거다)- 동적으로 엘리먼트를 추가할 때마다 핸들러를 고려할 필요가 없다. (
<li>네번째</li>
를 추가하고 싶으면 이벤트를 뭐 줄 지 생각을 굳이 하지 않고도 추가하면 된다)- 메모리에 있게되는 이벤트 핸들러가 적어지기 때문에 퍼포먼스 측면에서 이점이 있다.
하지만 무조건 적으로 이벤트 위임을 사용하게 되면 내가 프로젝트에서 겪었던 오류(해당 이벤트를 클릭했는데, 엉뚱한 이벤트가 발생해서 도대체 이게 무슨일이지?했음)를 찾아내기까지 꽤 오랜 시간을 겪고 혼란스러울 수 있기 때문에 상황에 맞게 잘 사용하는게 중요한것 같다.
마무리로 '이벤트 버블링,캡쳐링을 신경쓰고 싶지 않다' 혹은 '원치 않은 이벤트 버블링,캡쳐링이 발생했는데 이를 멈추고 싶다'하면 event.stopPropagation()
을 사용하면 된다!
// 이벤트 캡쳐 예제코드, (자바스크립트로)
divs.forEach(function(div) {
div.addEventListener('click', logEvent, {
capture: true // default 값은 false입니다.(버블링이 디폴트)
});
});
function logEvent(event) {
event.stopPropagation();
console.log(event.currentTarget.className); // one
}
참고자료