버블링 : 자식 요소에서 발생한 이벤트가 부모 요소로 전파
캡쳐링 : 자식 요소에서 발생한 이벤트가 부모 요소부터 시작해 이벤트를 발생시킨 자식 요소까지 도달
<body>
<div class="one"> one
<div class="two"> two
<div class="three"> three
</div>
</div>
</div>
</body>
이벤트가 발생한 요소부터 해당 요소의 부모들에게 차례대로 이벤트가 전파되는 방식
⇒ 이벤트가 발생한 요소에 할당된 핸들러가 동작 ⇒ 해당 요소의 부모들의 핸들러 역시 차례대로 실행되어 최상단의 document를 만날 때까지 진행
let divs = document.querySelectorAll('div');
divs.forEach(function(div) {
div.addEventListener('click', logEvent);
});
function logEvent(event) {
console.log(event.currentTarget.className);
}
⇒ three를 클릭하면 three > two > one 출력
⇒ two를 클릭하면 two > one 출력
⇒ one 클릭하면 one 출력
캡쳐링은 버블링과 반대 방향으로 진행되는 이벤트 전파 방식
⇒ 이벤트 발생했을 때 최상위 요소인 body 태그에서 해당 태그를 찾아 내려감
let divs2 = document.querySelectorAll('div');
divs2.forEach(function(div) {
div.addEventListener('click', logEvent, {
capture: true // default 값은 false입니다.
});
});
function logEvent(event) {
console.log(event.currentTarget.className);
}
⇒ three를 클릭하면 one > two > three 출력
⇒ two 클릭 하면 one > two 출력
⇒ one 클릭 하면 one 출력
event.preventDefault는 고유 동작을 중단시키고
event.stopPropagation 는 상위 엘리먼트들로의 이벤트 전파를 중단시킨다.
해당 이벤트가 전파되는 것을 막음
원하는 화면 요소의 이벤트만 신경 쓰고 싶다면 사용
function logEvent(event) {
event.stopPropagation();
}
버블링의 경우 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하지 않고
캡쳐링의 경우 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들에게 이벤트를 전달하지 않음
⇒ 위 버블링과 캡쳐링 예제에서 logEvent 에 위 event.stopPropagation()
를 추가했다면 버블링은 three
를 캡쳐링은 one
을 출력
고유의 기능(동작)을 중단시키고 이벤트를 취소하는 역할
하위 요소에 각각 이벤트를 붙이지 않고
상위 요소에 하위 요소의 이벤트들을 제어하는 방식
할 일 목록의 체크박스 클릭 했을 때 이벤트 리스터가 동작하는데
<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>
let inputs = document.querySelectorAll('input'); // 모든 input 선택
inputs.forEach(function(input) {
input.addEventListener('click', function(event) {
alert('clicked'); // input 클릭 시 얼럿 노출
});
});
// 새 리스트 아이템을 추가하는 코드
let itemList = document.querySelector('.itemList');
let li = document.createElement('li'); // li 엘리먼트 생성
let input = document.createElement('input'); // input 생성
let label = document.createElement('label'); // label 생성
let labelText = document.createTextNode('이벤트 위임 학습'); // 선택한 요소에 텍스트 추가
input.setAttribute('type', 'checkbox'); // input 요소에 type 속성을 checkbox 로 적용
input.setAttribute('id', 'item3'); // input 요소에 id 속성을 item3 로 적용
label.setAttribute('for', 'item3'); // label 요소에 for 속성을 item3 로 적용
label.appendChild(labelText); // label 안에 labelText 추가
li.appendChild(input); // li 안에 input 추가
li.appendChild(label); // li 안에 label 추가
itemList.appendChild(li); // itemList 안에 li 추가
이렇게 새로 li를 추가한 경우 클릭 이벤트 리스너는 동작하지 않는다.
⇒ 위에 코드를 살펴보면, 인풋 박스에 클릭 이벤트 리스너를 추가한 시점에 li 아이템은 두개이니, 새롭게 추가된 li에는 클릭 이벤트 리스너가 등록되지 않았다.
⇒ 그럼 매번 새롭게 추가된 li에 클릭 이벤트 리스너를 달아줘야 될까?
⇒ 이 작업을 해결할 수 있는게 이벤트 위임(Event Delegation)
itemList.addEventListener('click', function(event) {
alert('clicked');
});
⇒ 맨 위 html 밑에 있는 js 코드를 지워버리고
인풋 박스의 상위 요소인 ul 태그(.itemList) 에 이벤트 리스너
를 달고,
하위에서 발생한 클릭 이벤트를 감지 하는 위 코드를 추가한 후 ( 이벤트 버블링 )
새 리스트 아이템을 추가하는 코드를 넣는다면 이후에 추가되는 li 들도 이벤트 리스너가 잘 작동할 것