특정 위치에서 발생한 이벤트가 최상위 요소까지 올라가며 연속으로 발생하는 현상을 의미한다.
<body>
<div class="one">
<div class="two">
<div class="three">
</div>
</div>
</div>
</body>
var divs = document.querySelectorAll('div');
divs.forEach(function(div) {
div.addEventListener('click', logEvent);
});
function logEvent(e) {
console.log(e.currentTarget.className);
}
세 개의 div 태그에 모두 클릭 이벤트를 등록하고 클릭 시 logEvent 함수를 실행하는 코드이다.
최하위 요소인 <div class="three"></div>
를 클릭하면 콘솔에 다음처럼 찍힌다.
three div 하나밖에 클릭하지 않았는데도 위의 부모요소에 등록된 클릭 이벤트들이 전부 실행되었다.
이때 주의할 점은 클릭한 곳으로부터 상위로 올라간다는 것이다.
이렇게 이벤트 버블링이 발생하게 되는 이유는 두 가지이다.
👆 브라우저가 기본적으로, 특정 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 요소까지 전파시키는 이벤트 버블링적 속성을 갖고 있어서이다.
(* 이벤트 캡처링의 경우 이벤트 리스너의 옵션을 바꿔줘야 발생한다)
✌ div 태그들이 모두 부모-자식 관계이고, 각 div에 모두 이벤트가 등록되어 있어서이다.
(만약 이벤트가 특정 div 에만 달려 있다면 이벤트 버블링이 발생하지 않는다)
이벤트 버블링과 반대로, 이벤트 발생 시 최상단 요소에서 이벤트가 발생한 위치까지 내려가며 연속으로 이벤트가 발생하는 현상이다.
<body>
<div class="one">
<div class="two">
<div class="three">
</div>
</div>
</div>
</body>
var divs = document.querySelectorAll('div');
divs.forEach(function(div) {
div.addEventListener('click', logEvent, {
capture: true // default 값은 false
});
});
function logEvent(event) {
console.log(event.currentTarget.className);
}
이벤트 캡처링은 브라우저의 기본적 속성이 아니기 때문에 이벤트 리스너의 옵션에 capture: true
를 설정해줘야 발생한다.
그리고 아까처럼 <div class="three"></div>
를 클릭하면 콘솔에는 다음과 같이 나타난다.
이벤트 버블링과 달리, three를 눌렀음에도 최상단인 one 부터 실행되는 것을 볼 수 있다. (two를 누르면 one부터 시작해서 two까지 찍힌다)
어디를 눌러도 무조건 최상단의 이벤트부터 시작되어 누른 데까지 실행된다는 걸 알 수 있다.
이벤트 버블링과 캡처링 현상이 발생하지 않게 하려면
이벤트 발생 시 실행되는 함수의 첫 줄에 e.stopPropagation()
을 넣어주면 된다.
function logEvent(e) {
e.stopPropagation();
}
stopPropagation()은 이벤트가 전파되는 것을 막아주는 웹 API이다.
이 코드를 쓰면 아래로든 위로든 이벤트가 퍼져나가지 않고
이벤트 발생 시 무조건 첫번째 발생한 이벤트만 실행한다.
(위의 예제에서는 버블링의 경우 three만 출력, 캡처링의 경우 one만 출력)
이벤트 버블링과 캡쳐링은 이벤트 위임을 위한 선수 지식에 가깝다.
이벤트 위임은 자바스크립트로 코드를 작성할 때 자주 쓰게 되는 개념이다.
정의를 하자면 ‘하위 요소 각각에 이벤트를 붙이지 않고 상위 요소에만 이벤트를 등록하여 하위 요소까지 퍼져나가게 하는 방식'이다.
다음과 같이 할 일 checkbox에 체크하는 코드가 있다.
<h1>오늘의 할 일</h1>
<ul class="itemList">
<li>
<input type="checkbox" id="item1">
<label for="item1">이벤트 버블링 학습</label>
</li>
<li>
<input type="checkbox" id="item2">
<label for="item2">이벤트 캡쳐 학습</label>
</li>
</ul>
querySelectorAll을 통해 체크박스 인풋 하나하나에 클릭 이벤트를 달아주면
인풋 박스가 추가될 때마다 매번 이벤트를 달아줘야 한다.
var inputs = document.querySelectorAll('input');
inputs.forEach(function(input) {
input.addEventListener('click', function(event) {
alert('clicked');
});
});
하지만 이벤트 위임을 활용하여 인풋 박스의 상위 요소인 ul 태그 하나에만 이벤트 리스너를 달아주면 이벤트 버블링 현상이 발생하여
하위의 인풋 박스 중 어느 것을 눌러도 상단의 ul 태그까지 클릭 이벤트가 버블링되어 올라가면서 상단 ul의 이벤트 리스너가 하위의 이벤트를 감지할 수 있게 된다.
var itemList = document.querySelector('.itemList');
itemList.addEventListener('click', function(event) {
alert('clicked');
});
이제 리스트 아이템을 새로 추가할 때마다 클릭 이벤트를 매번 안 달아도 된다 🧚♀️