Capturing 과 Bubbling
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .outer { width: 500px; height: 500px; background-color: yellow; } .middle { width: 50%; height: 50%; margin: auto; background-color: thistle; transform: translateY(50%); } button { position: relative; top: 50%; left: 50%; transform: translate(-50%, -50%); } </style> </head> <body> <div class="outer"> <div class="middle"> <button>Click Me</button> </div> </div> <script> const outer = document.querySelector('.outer'); const middle = document.querySelector('.middle'); const button = document.querySelector('button'); outer.addEventListener('click', event => { console.log(`outer: ${event.currentTarget}, ${event.target}`); }); middle.addEventListener('click', event => { console.log(`middle ${event.currentTarget}, ${event.target}`); }); button.addEventListener('click', event => { console.log(`button1 ${event.currentTarget}, ${event.target}`); }); button.addEventListener('click', event => { console.log(`button2 ${event.currentTarget}, ${event.target}`); }); </script> </body> </html>
상기 코드에서 버튼을 클릭하면, 부모의 이벤트까지 모두 호출되는걸 확인할 수 있다.
이는 bubbling up이 되는 것이다.
버블링을 막는 방법 ? 🚫
button.addEventListener('click', event => { console.log(`button1 ${event.currentTarget}, ${event.target}`); event.stopPropagation(); // 위로 버블링이 일어나지않게 처리 });
위와 같이 버블링이 일어나지않도록 처리할 수 있다. 하지만, 이렇게 처리할경우 여전히 button1, button2가 출력된다.
button.addEventListener('click', event => { console.log(`button1 ${event.currentTarget}, ${event.target}`); event.stopImmediatePropagation(); // 실제로 이벤트가 일어난 엘리멘트만 핸들링 });
event.stopImmediatePropagation() 로 실제로 이벤트가 일어난 엘리멘트만 핸들링이 가능하다.
하지만 버블링을 stopPropagation() 로 처리하는것은 좋지 않다 (위험하다)
예를 들어, 부모 컨테이너에서 자식 이벤트가 발생할때마다 중요한 로직이 발생한다고 할때 자식에서만 처리하고 stopPropagation() 한다는 것은 정말 위험하다.
이상적인 방법 ✅
부모에서 막는 방법이 좋다.
이벤트에 있는 타겟이 일어난 위치와 이벤트에 있는 현 타겟이 다르면 이벤트를 발생시키지 않는 방식으로 처리
const outer = document.querySelector('.outer'); const middle = document.querySelector('.middle'); const button = document.querySelector('button'); outer.addEventListener('click', event => { if (event.target !== event.currentTarget) { return; } // 이벤트가 발생한곳과 현재 이벤트타겟이 다를때 실행취소 console.log(`outer: ${event.currentTarget}, ${event.target}`); }); middle.addEventListener('click', event => { if (event.target !== event.currentTarget) { return; } console.log(`middle ${event.currentTarget}, ${event.target}`); }); button.addEventListener('click', event => { console.log(`button1 ${event.currentTarget}, ${event.target}`); }); button.addEventListener('click', event => { console.log(`button2 ${event.currentTarget}, ${event.target}`); });