이벤트 버블링, 이벤트 캡처링과 이벤트 위임

NSH·2022년 6월 20일
0

이벤트 등록

이벤트 등록은 웹 애플리케이션에서 사용자의 입력을 받기 위해서 필요한 기능이다.

<button>add Item</button>

let addItem = event => console.log(event);

let button = document.querySelector('button');

button.addEventListener('click', addItem);

위 예제는 add Item 버튼을 클릭하면 addItem 함수가 실행되어 event 파라미터를 출력하여 이벤트에 대한 정보를 확인할 수 있다.

addEventListener() Web API는 개발자들이 화면에 동적인 기능을 추가하기 위해서 자연스럽게 접하게 되는 기능이다. 브라우저가 어떻게 이벤트를 감지하는지 2가지 방식에 대해서 알아보자.

이벤트 버블링(Event Bubbling)

상위 화면 요소
HTML은 트리 구조를 가지고 있으며 트리 구조상 한 단계 위에 있는 요소를 상위 요소라고 부른다.

이벤트 버블링은 특정 화면 요소에서 이벤트가 발생했을 때 상위 화면 요소들로 전달되어 가는 특성이다.

위 그림은 아래의 예시를 도식화한 그림이다.

<body>
  <div class="one">
    <div class="two">
      <div class="three">
      </div>
    </div>
  </div>
</body> 

let divs = document.querySelectorAll('div');

let logEvent = event => console.log(event.currentTarget.className);

divs.forEach(div => {
	div.addEventListener('click', logEvent);
})

<div class="three"/> 를 클릭하면 아래와 같은 결과가 나온다.

<div class="three"/> 만 클릭했을 뿐인데 3개의 이벤트가 발생한 이유는 브라우저가 이벤트를 감지하는 방식때문이다.

브라우저는 이벤트가 발생했을 때 이벤트를 최상위에 있는 화면 요소까지 이벤트를 전파한다.

하위 요소에서 상위 요소로 이벤트가 전파되는 방식을 이벤트 버블링 이라고 부른다.

이벤트 캡처링(Event Capturing)

이벤트 캡처링은 이벤트 버블링과 반대 방향으로 이벤트가 전파된다.

이벤트가 발생했을 때 최상위 요소인 body 태그에서 해당 태그를 찾아 내려간다.

코드로는 이벤트 버블링 코드와 대부분 동일하고 addEventListener() API 옵션 객체에 capture: true를 설정해주면 이벤트를 감지하기 위해 이벤트 버블링과 반대 방향으로 탐색한다.

<body>
  <div class="one">
    <div class="two">
      <div class="three">
      </div>
    </div>
  </div>
</body> 

let divs = document.querySelectorAll('div');

let logEvent = event => console.log(event.currentTarget.className);

divs.forEach(div => {
	div.addEventListener('click', logEvent, {
		capture: true // default: false
	});
})
를 클릭하면 아래와 같은 결과가 나오는걸 볼 수 있다.

event.stopPropagation()

event.stopPropagation() Web API를 사용하면 이벤트가 전파되는 것을 막을 수 있다.

이벤트 버블링의 경우에는 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하는 것을 막을 수 있다.

이벤트 캡처링의 경우에는 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들로 이벤트를 전달하지 않는다.

// 이벤트 버블링 예제
let divs = document.querySelectorAll('div');

let logEvent = (event) => {
	event.stopPropagation();
    console.log(event.currentTarget.className);
}

divs.forEach(div => {
	div.addEventListener('click', logEvent);
});

// 이벤트 캡처링 예제
let divs = document.querySelectorAll('div');

let logEvent = (event) => {
	event.stopPropagation();
    console.log(event.currentTarget.className);
}

divs.forEach(div => {
	div.addEventListener('click', logEvent, {
    	capture: true
    });
});

이벤트 위임(Event Delegation)

이벤트 버블링과 캡처링은 이벤트 위임을 위한 선수 지식이다. 바닐라 JS로 웹 앱을 구현할 때 자주 사용되는 패턴이다.

이벤트 위임은 하위 요소들에 각각 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트를 제어하는 방식이다.

<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');
inputs.forEach((input) => {
	input.addEventListener('click', () => {
    	alert('clicked');
    })
});

위 예제 코드를 실행하면 문제없이 잘 동작한다.

만약 여기서 할 일이 더 생겨 리스트 아이템을 추가하면 클릭 이벤트가 정상적으로 동작하지 않는다.

// 리스트 아이템을 추가하는 코드
let itemList = document.querySelector('.itemList');
let li = document.createElement('li');
let input = document.createElement('input');
let label = document.createElement('label');
let labelText = document.createTextNode('이벤트 위임 학습');

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

이유는 새롭게 추가된 아이탬에는 클릭 이벤트 리스너가 등록되지 않았다.

새롭게 아이템이 추가될 때마다 이벤트 리스너를 일일이 달아주면 작업이 매우 번거롭고 모든 아이템에 이벤트 리스너가 등록되면 메모리도 많이 잡아먹을 수 있다.

위에 언급한 문제들은 이벤트 위임으로 해결할 수 있다.

// 모든 아이템에 이벤트 리스너를 등록하는 코드 주석 처리
// let inputs = document.querySelectorAll('input');
// inputs.forEach((input) => {
// 	input.addEventListener('click', () => {
//     	alert('clicked');
//    })
// });

// 상위 요소인 ul 태그에 이벤트 리스너를 등록
let itemList = document.querySelector('.itemList;
itemList.addEventListener('click', event => {
	alert('click');
});

상위 요소인 ul 태그에 이벤트 리스너를 등록하고 하위에서 발생한 이벤트 클릭 이벤트를 감지할 수 있기 때문에 아이템이 추가 될 때마다 이벤트를 등록할 필요가 없어진다.

참고
https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/

profile
잘 하고 싶다.

0개의 댓글