하단의 코드를 보면 더욱 자세히 이해할 수 있다.
<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(event) { console.log(event.currentTarget.className); }
세 개의 div 태그에 모두 클릭 이벤트를 등록하고 클릭했을 때 logEvent 함수를 실행시키는 코드이다.
여기서 위 그림대로 최하위 div 태그
<div class="three"> </div>
를 클릭하면 아래와 같은 결과가 실행된다.
three
two
one
브라우저가 이벤트를 감지하는 방식 때문이다.
브라우저는 특정 화면 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 화면 요소까지 이벤트를 전파시킨다.
따라서, 클래스 three -> two -> one 순서로 div 태그에 등록된 이벤트들이 실행되는 것!
각 태그마다 이벤트가 등록되어 있기 때문에 상위 요소로 이벤트가 전달되는 것을 확인할 수 있다.
하지만, 이벤트가 특정 div 태그에만 달려 있다면 위와 같은 동작 결과는 확인할 수 없다.
하단의 코드를 보면 이벤트 버블링과 다른 점을 발견할 수 있다.
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); }
addEventListener() API에서 옵션 객체에 capture:true를 설정해주면 해당 이벤트를 감지하기 위해 이벤트 버블링과 반대 방향으로 탐색한다.
이 경우에는 최하위 div 태그
<div class="three"> </div>
를 클릭하면 아래와 같은 결과가 실행된다.
one
two
three
하단의 코드로 이해해보자.
<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>
var inputs = document.querySelectorAll('input'); inputs.forEach(function(input) { input.addEventListener('click', function(event) { alert('clicked'); }); });
querySelectorAll() 를 이용해 화면에 존재하는 모든 인풋 박스 요소를 가져온 다음 각 인풋 박스의 요소에 클릭 이벤트 리스너를 추가한다.
-> 화면 실행 후 각 리스트 아이템의 인풋박스(체크박스)를 클릭하면 경고창이 표시된다.
새로운 리스트 아이템을 추가할 경우?
var itemList = document.querySelector('.itemList'); var li = document.createElement('li'); var input = document.createElement('input'); var label = document.createElement('label'); var labelText = document.createTextNode('이벤트 위임 학습'); input.setAttribute('type', 'checkbox'); input.setAttribute('id', 'item3'); label.setAttribute('for', 'item3'); label.appendChild(labelText); li.appendChild(input); li.appendChild(label); itemList.appendChild(li);
새로 추가된 리스트 아이템에는 클릭 이벤트 리스너가 동작하지 않는다. 클릭 이벤트 리스너가 추가된게 없으니까.
그러면 매번 새롭게 추가된 리스트 아이템까지 클릭 이벤트 리스너를 일일이 달아줘야 하나? 그것도 아니다.
var itemList = document.querySelector('.itemList'); itemList.addEventListener('click', function(event) { alert('clicked'); });
화면의 모든 인풋 박스에 일일이 이벤트 리스너를 추가하는 대신 인풋 박스의 상위 요소인 ul 태그, .itemList에 이벤트 리스너를 달아놓고 하위에서 발생한 클릭 이벤트를 감지한다(=이벤트 버블링).
해당 코드를 추가 후 실행시킬 경우, 각 아이템을 클릭할 때 마다 경고창(클릭 이벤트)이 알맞게 동작한다.
고로 리스트 아이템을 추가할 시 마다 클릭 이벤트를 다는 수고가 없어진다.
출처: 캡틴판교, yesdoing님 블로그