[Javascript] 이벤트 버블링과 캡쳐링
이벤트 버블링 (Event Bubbling)
- 한 요소에 이벤트가 발생하면 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작하고 최상단의 부모 요소를 만날 때까지 반복되면서 핸들러가 동작하는 현상을 이벤트 버블링이라고 한다.
- 즉, 특정 화면 요소에서 이벤트가 발생했을 때 해당 이벤트가 더 상위의 화면 요소들로 전달되어 가는 특성이다.
하단의 코드를 보면 더욱 자세히 이해할 수 있다.
<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
div 태그 한 개만 클릭했을 뿐인데 3개의 이벤트가 발생되는 이유?
브라우저가 이벤트를 감지하는 방식 때문이다.
브라우저는 특정 화면 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 화면 요소까지 이벤트를 전파시킨다.
따라서, 클래스 three -> two -> one 순서로 div 태그에 등록된 이벤트들이 실행되는 것!
각 태그마다 이벤트가 등록되어 있기 때문에 상위 요소로 이벤트가 전달되는 것을 확인할 수 있다.
하지만, 이벤트가 특정 div 태그에만 달려 있다면 위와 같은 동작 결과는 확인할 수 없다.
이벤트 캡쳐링 (Event Capturing)
- 이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식이다.
- 특정 이벤트가 발생했을 때 최상위 요소인 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); }
addEventListener() API에서 옵션 객체에 capture:true를 설정해주면 해당 이벤트를 감지하기 위해 이벤트 버블링과 반대 방향으로 탐색한다.
이 경우에는 최하위 div 태그
<div class="three"> </div>
를 클릭하면 아래와 같은 결과가 실행된다.
one
two
three
이벤트버블링을 막는 방법? e.stopPropagation()을 쓰자!
- 클릭한 타깃의 이벤트만 발생하고 상위 요소로 이벤트가 전파되는 것을 막으려면 e.stopPropagation() 을 사용하면 된다.
- 이벤트 버블링의 경우: 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하는 것을 방해한다.
- 이벤트 캡쳐링의 경우: 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들로 이벤트를 전달하지 않는다.
이벤트 위임 (Event Delegation)도 알고 가자.
- 캡쳐링과 버블링을 이용한 것으로, 부모 안에 있는 자식 요소들에(여러 엘리먼트) 각각 이벤트 리스너를 추가하지 않고, 공통되는 부모에 이벤트 리스너를 등록하여 자식 요소의 이벤트를 제어하는 방식이다.
- 캡쳐링과 버블링을 이용한 것으로, 부모 안에 있는 자식 요소들에(여러 엘리먼트) 각각 이벤트 리스너를 추가하지 않고, 공통되는 부모에 이벤트 리스너를 등록하여 자식 요소의 이벤트를 제어하는 방식이다.
하단의 코드로 이해해보자.
<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님 블로그
Author And Source
이 문제에 관하여([Javascript] 이벤트 버블링과 캡쳐링), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hyejin4169/Javascript-이벤트-버블링과-캡쳐링저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)