버블링과 캡쳐링 & 이벤트 위임

moono·2023년 1월 21일
0

JavaScript

목록 보기
16/23

버블링 : 자식 요소에서 발생한 이벤트가 부모 요소로 전파
캡쳐링 : 자식 요소에서 발생한 이벤트가 부모 요소부터 시작해 이벤트를 발생시킨 자식 요소까지 도달

<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 출력


stopPropagation & preventDefault

event.preventDefault는 고유 동작을 중단시키고
event.stopPropagation 는 상위 엘리먼트들로의 이벤트 전파를 중단시킨다.

event.stopPropagation()

해당 이벤트가 전파되는 것을 막음
원하는 화면 요소의 이벤트만 신경 쓰고 싶다면 사용

function logEvent(event) {
	event.stopPropagation();
}

버블링의 경우 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하지 않고
캡쳐링의 경우 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들에게 이벤트를 전달하지 않음
⇒ 위 버블링과 캡쳐링 예제에서 logEvent 에 위 event.stopPropagation() 를 추가했다면 버블링은 three캡쳐링은 one 을 출력

event.preventDefault()

고유의 기능(동작)을 중단시키고 이벤트를 취소하는 역할

  • a 태그를 눌렀을때 href 링크로 이동하지 않게 할 경우
  • form 안에 submit 역할을 하는 버튼을 눌렀어도 새로 실행하지 않게 하고 싶은 경우(submit은 작동 됨)


이벤트 위임(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>
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 들도 이벤트 리스너가 잘 작동할 것

이 내용으로 확인했따!


Reference

0개의 댓글