[JavaScript] 이벤트 전파

Letmegooutside·2022년 1월 28일
2

JavaScript

목록 보기
21/25
post-thumbnail

이벤트 버블링 (Event Bubbling)

특정 화면 요소에서 이벤트가 발생했을 때 해당 이벤트가 상위의 화면 요소들로 전달되어 가는 특성

<body>
	<div class="one"> one
		<div class="two"> two
			<div class="three"> three
			</div>
		</div>
	</div>
</body>
<script>
  var divs = document.querySelectorAll('div');
  divs.forEach(function(div) {
      div.addEventListener('click', logEvent);
  });

  function logEvent(event) {
      console.log(event.currentTarget.className);
  }
</script>

위 코드는 중첩된 세 개의 div코드에 모두 클릭 이벤트를 등록하고 클릭했을 때 logEvent함수를 실행시키는 코드이다.

여기서 최하위 div 태그인 <div class="three">를 클릭하면 위와 같이 결과가 실행된다.

위 코드를 도식화하면 아래와 같다.

div태그 한 개만 클릭했을 뿐인데 왜 3개의 이벤트가 발생되는 이유는 브라우저가 이벤트를 감지하는 방식 때문이다.

브라우저는 특정 화면 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 화면 요소까지 전파시킨다.
따라서 클래스면 three → two → one 순서로 div태그에 등록된 이벤트들이 실행된다.
마찬가지로 two 클래스를 갖는 두 번째 태그를 클릭했다면 two → one의 순으로 클릭 이벤트가 동작할 것이다.

여기서 주의할 점은 각 태그마다 클릭 이벤트가 등록되어 있기 때문에 상위 요소로 이벤트가 전달되는 것이다.
만약 이벤트가 특정 div태그에만 달려있었다면 위와 같은 동작 결과는 확인할 수 없다.

이벤트 캡쳐 (Event Capture)

이벤트 버블링과 반대방향으로 진행되는 이벤트 전파 방식

이벤트 캡처는 addEventListener()에서 옵션 객체에 capture:true를 설정하여 구현할 수 있다.
그러면 해당 이벤트를 감지하기 위해 이벤트 버블링과 반대 방향으로 탐색한다.

<body>
	<div class="one"> one
		<div class="two"> two
			<div class="three"> three
			</div>
		</div>
	</div>
</body>
<script>
  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);
  }
</script>

따라서 이벤트 버블링에서의 예시와 동일하게 <div class="three">태그를 클릭해도 아래와 같은 결과가 나타난다.

event.stopPropagation()

stopPropagation() 은 해당 이벤트가 전파되는 것을 막는다.

  • 이벤트 버블링 : 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트 전달 방지
  • 이벤트 캡처 : 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소로 이벤트 전달 방지

따라서 위의 예제의 logEvent함수에 stopPropagation() API를 사용한다면 이벤트 버블링 예제에서는 three, 이벤트 캡처 예제에서는 one이 출력될것이다.

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

이벤트 버블링

이벤트 캡처링

이벤트 위임 (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) {
      console.log('clicked');
	});
});

할 일 목록을 간단한 리스트 아이템으로 나타낸 코드이다.

querySelectorAll()을 이용해 화면에 존재하는 모든 input요소를 가져온 다음 각 요소에 클릭 이벤트 리스너를 추가한다.
화면을 실행시키고 각 리스트 아이템의 체크 박스를 클릭하면 아래와 같이 핸들러가 동작한다.

그런데 만약 여기서 리스트 아이템을 더 추가한다면 매번 새롭게 추가된 리스트 아이템까지 클릭 이벤트 리스너를 일일이 달아줘야 한다.

// ...

// 새 리스트 아이템을 추가하는 코드
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');
// 이벤트 리스너 추가
input.addEventListener('click', function(event) {
  console.log('clicked');
});
label.setAttribute('for', 'item3');
label.appendChild(labelText);
li.appendChild(input);
li.appendChild(label);
itemList.appendChild(li);

리스트 아이템이 많아지면 많아질수록 이벤트 리스너를 다는 작업 자체가 매우 번거로워지는데, 이를 해결할 수 있는 방법이 이벤트 위임이다.

위 코드를 아래와 같이 변경해보자

// ul태그에 이벤트 리스너 추가
var itemList = document.querySelector('.itemList');
itemList.addEventListener('click', function(event) {
  console.log('clicked');
});

화면의 모든 input요소에 이벤트 리스너를 추가하는 대신, input요소들의 상위 요소인 ul태그, .itemList에 이벤트 리스너를 달아놓고 하위에서 발생한 클릭 이벤트를 감지한다. (이벤트 버블링)

결과는 아래와 같다.

장점

  • 많은 핸들러를 할당하지 않아도 되기 때문에 초기화가 단순해지고 메모리가 절약된다.
  • 요소를 추가하거나 제거할 때 해당 요소에 할당된 핸들러를 추가하거나 제거할 필요가 없기 때문에 코드가 단순해진다.

단점

  • 이벤트 위임을 사용하려면 이벤트가 반드시 버블링 되어야하지만 몇몇 이벤트는 버블링 되지 않는다.
  • 컨테이너 수준에 할당된 핸들러가 응답할 필요가 있는 이벤트이든 아니든 상관없이 모든 하위 컨테이너에서 발생하는 이벤트에 응답해야 하므로 CPU 작업 부하가 늘어날 수 있다. (이런 부하는 무시할만한 수준이므로 실제로는 잘 고려하지 않는다.)



Reference
https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/
https://ko.javascript.info/event-delegation

0개의 댓글