자바스크립트의 이벤트 전달 방식 - 이벤트 버블링, 이벤트 캡쳐, 이벤트 위임

danmin20·2021년 2월 21일
0

Javascript

목록 보기
3/9

자바스크립트에서의 이벤트 객체란 돔 내에 위치한 이벤트를 말하며, 사용자가 현재 취한 액션에 대한 상세정보를 담고 있다.
웹 애플리케이션에서 사용자의 입력을 받기 위해 이벤트 등록을 하게 되면, 이후 이벤트가 발생했을 때 브라우저가 이를 감지하게 된다.

브라우저가 이러한 이벤트를 감지하는 방식에는 두 가지가 있다. 바로 버블링과 캡쳐이다.
DOM은 트리구조를 갖는데, 트리의 노드를 타고 이벤트가 전파되는 형식이다.

Event Bubbling

이벤트 버블링은 특정 화면 요소에서 이벤트가 발생했을 때, 해당 이벤트가 더 상위의 화면 요소들로 전달되는 현상이다.
따라서 브라우저는 특정 화면 요소에서 이벤트가 발생했을 때, 해당 이벤트를 최상위의 화면 요소까지 전파시킨다.

Event Capture

특정 이벤트가 전달되서 최상위 요소에서 감지되면, 다시 이벤트가 발생한 태그를 찾아 내려가게 된다. 이러한 현상이 이벤트 캡쳐이다.
이벤트리스너 api에서 옵션 객체에 capture: true를 설정해주면 버블링과 반대 방향인 캡쳐 방식으로 탐색된다.

event.stopPropagation()

이벤트를 원하는 화면 요소에서만 신경쓰고 싶을 때 쓸 수 있는 것이 바로 stopPropagation() 웹api이다.
버블링의 경우에는 해당 요소의 이벤트만 발생시키고 상위 요소로 전달되는 것을 막으며,
캡쳐의 경우에는 최상위 요소의 이벤트만 발생시키고 하위 요소로 전달되는 것을 막는다.

code example

<body>
	<div class="a">
		<div class="b">
			<div class="c">
			</div>
		</div>
	</div>
</body>
var divs = document.querySelectorAll('div');

divs.forEach(function(div) {					// c b a
	div.addEventListener('click', logEvent);
});

divs.forEach(function(div) {					// a b c
	div.addEventListener('click', logEvent, {
		capture: true
	});
});

divs.forEach(function(div) {					// c
	div.addEventListener('click', logStopPropagation);
});

divs.forEach(function(div) {					// a
	div.addEventListener('click', logStopPropagation, {
		capture: true
	});
});

function logEvent(event) {
	console.log(event.currentTarget.className);
}

function logStopPropagation(event) {
	event.stopPropagation();
	console.log(event.currentTarget.className);
}

Event Delegation

이벤트 위임은 바닐라 자바스크립트에서 많이 사용되는 코딩 패턴인데, 하위 요소에 각각 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트들을 제어하는 방법이다.

여러 개의 인풋 박스가 있는 리스트를 예로 들어보자.
인풋 박스에 일일히 이벤트 리스너를 추가하는 대신 ul 태그에 이벤트 리스너를 달아서 버블링을 이용하여 하위 인풋 태그의 클릭 이벤트를 감지하도록 하면,
이벤트 리스너 등록 이후에 인풋 박스가 추가적으로 생성되더라도 클릭 이벤트를 정상적으로 동작시킬 수 있을 것이다.

<h1>오늘의 할 일</h1>
<ul class="itemList">
	<li>
		<input type="checkbox" id="item1">
		<label for="item1">first item</label>
	</li>
	<li>
		<input type="checkbox" id="item2">
		<label for="item2">second item</label>
	</li>
</ul>
var inputs = document.querySelectorAll('input');

위와 같은 코드가 있을 때,
인풋 박스에 일일히 이벤트 리스너를 추가하게 되면 아래와 같다.

inputs.forEach(function(input) {
	input.addEventListener('click', function(event) {
		alert('clicked');
	});
});
var itemList = document.querySelector('.itemList');

ul 태그에 이벤트 리스너를 달아서 버블링을 이용하게 되면 아래와 같다.

var itemList = document.querySelector('.itemList');
itemList.addEventListener('click', function(event) {
	alert('clicked');
});

새로운 인풋 요소를 아래와 같이 추가한다고 했을 때,

var li = document.createElement('li');
var input = document.createElement('input');
var label = document.createElement('label');
var labelText = document.createTextNode('third item');

input.setAttribute('type', 'checkbox');
input.setAttribute('id', 'item3');
label.setAttribute('for', 'item3');
label.appendChild(labelText);
li.appendChild(input);
li.appendChild(label);
itemList.appendChild(li);

첫 번째 방법의 경우에는 third item에 대한 클릭 이벤트가 작동하지 않는 반면,
두 번째 방법으 경우에는 잘 작동하는 것을 볼 수 있을 것이다.

Reference

  • https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/
profile
FE developer 😉

0개의 댓글