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

TheJang·2021년 6월 20일
0

HTML/CSS/FE

목록 보기
1/2
post-thumbnail

이번 포스팅에서는 이벤트 버블링과 이벤트 캡처, 이벤트 위임에 대해 알아보겠습니다.

📌 이벤트 등록?

이벤트 등록이란?

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

const button = document.querySelector('button');
button.addEventListener('click', addItem);

function addItem(event) {
	console.log(event);
}

이처럼 addEventListener() 웹 API는 웹 개발자들이 화면에 동적인 기능을 추가하기 위해 자연스럽게 접하게 되는 기본적인 기능입니다. 사용자의 입력에 따라 추가 동작을 구현할 수 있는 방법이죠. 여기서 브라우저는 어떻게 이벤트의 발생을 감지했을까요? 브라우저가 이벤트를 감지하는 방식 2가지를 아래에서 알아보겠습니다.

📌 이벤트 버블링

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

상위의 화면 요소란? HTML 요소는 기본적으로 트리 구조를 갖습니다. 여기서는 트리 구조상 한 단계 위에 잇는 요소를 상위 요소라고하며, Body 태그를 최상위 요소라고 부릅니다.

const divs = document.querySelectorAll('div');
divs.forEach(function(div) {
	div.addEventListener('click', logEvent);
});

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

브라우저는 특정 화면 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 화면 요소까지 이벤트를 전파시킵니다. 따라서, 클래스명 three→ two→ one 순서로 div 태그에 등록된 이벤트들이 실행됩니다.

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

이와 같은 하위에서 상위 요소로의 이벤트 전파 방식을 이벤트 버블링이라고 합니다.

📌 이벤트 캡처링

이벤트 캡처는 이벤트 버블링과 반대 방향으로 진행되는 이벤트 전파 방식입니다.

위 그림처럼 특정 이벤트가 발생했을 때, 최상위 요소인 Body 태그에서 해당 태그를 찾아 내려갑니다.

✅ 이벤트 캡처 구현

<body>
  <div class="one">
    <div class="two">
      <div class="three">
      </div>
    </div>
  </div>
</body>
const divs = document.querySelectorAll('div');
divs.forEach(function(div) {
	div.addEventListener('click', logEvent, {
		capture: true// default 값은 false입니다.});
});

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

addEventListener() API에서 옵션 객체에 capture: true를 설정해주면 됩니다.

📌 이벤트 전파 방지

브라우저의 기본 이벤트 혹은 버블링의 현상을 방지하고 싶은 상황이 있습니다. 이를 방지하기 위한 방법도 여러가지가 있습니다.

상황에 맞는 방법을 통해 추후 발생할 문제점을 방지해야합니다.

✅ event.preventDefault()

콜백함수에 event.preventDefault() 을 사용하면 브라우저의 기본 동작을 막을 수 있습니다. event.preventDefault()을 통해서 기본 동작을 멈춘 상태에서 event.defaultPrevented의 값은 true됩니다. 이것을 이용해서 추가적인 조건도 걸어줄 수 있습니다.

✅ event.stopPropagation()

원하는 화면 요소의 이벤트만 신경 쓰고 싶은 경우, 즉 이벤트 전파를 방지하고 싶은 경우 stopPropagation() 웹 API를 사용합니다.

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

위 API는 해당 이벤트가 전파되는 것을 막습니다. 따라서, 이벤트 버블링의 경우에는 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하는 것을 방해합니다. 그리고 이벤트 캡쳐의 경우에는 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들로 이벤트를 전달하지 않습니다.

// 이벤트 버블링 예제
divs.forEach(function(div) {
	div.addEventListener('click', logEvent);
});

function logEvent(event) {
	event.stopPropagation();
	console.log(event.currentTarget.className); // three
}
// 이벤트 캡쳐 예제
divs.forEach(function(div) {
	div.addEventListener('click', logEvent, {
		capture: true // default 값은 false입니다.
	});
});

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

❗️ event.stopPropagation()의 위험한 점

이벤트 전파를 막는 경우는 흔하지 않으며, 막게 된다면 event.stopPropagation()이 작성된 후, 상위 또는 하위 있는 event는 죽은 영역 (deadzone)이 됩니다.

예를 들어 document단에서 모든 click 이벤트를 감지하고 싶은데, stopPropagation에 의해 deadzone이 되면 감지하지 못하게 됩니다.

event.stopPropagation()의 문제를 event.preventDefault()와 event.defaultPrevented를 이용해 해결 할 수 있습니다.

먼저 이벤트가 발생한 곳에 event.preventDefault()가 실행된다면 event.defaultPrevented값이 true로 변경된다는 점을 이용합니다.

📌 이벤트 위임

이벤트 위임은 비슷한 방식으로 여러 요소를 다뤄야 할 때 사용됩니다.

사용자의 액션에 의해 이벤트 발생 시 이벤트는 document 레벨까지 버블링 되어 올라가집니다. 이 때문에 자식 엘리먼트에서 발생하는 이벤트를 부모 엘리먼트에서도 감지할 수 있습니다. 이러한 동작을 이용해 사용할 수 있는 방법이 이벤트 위임입니다. 특정 엘리먼트에 하나하나 이벤트를 등록하지 않고 하나의 부모에 등록하여 부모에게 이벤트를 위임하는 방법이 바로 그것입니다.

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

✅ 이벤트 위임의 이점

이벤트 위임의 이점은 다음 4가지로 정리할 수 있습니다.

  • 동적인 엘리먼트에 대한 이벤트 처리가 수월하다.
  • 상위 엘리먼트에서만 이벤트 리스너를 관리하기 때문에 하위 엘리먼트는 자유롭게 추가 삭제할 수 있다.
  • 이벤트 핸들러 관리가 쉽다.
  • 동일한 이벤트에 대해 한 곳에서 관리하기 때문에 각각의 엘리먼트를 여러 곳에 등록하여 관리하는 것보다 관리가 수월하다.
  • 메모리 사용량이 줄어든다.
    • 메모리 누수 가능성이 줄어든다.
    • 등록 핸들러 자체가 줄어들기 때문에 메모리 누수 가능성도 줄어든다

정리
1. 많은 핸들러를 할당하지 않아도 되기 때문에 초기화가 단순해지며, 메모리가 절약된다.
2. 요소를 추가하거나 제거할 때 해당 요소에 할당된 핸들러를 추가하거나 제거할 필요가 없기 때문에 코드가 짧아진다.
3. innerHTML이나 유사한 기능을 하는 스크립트로 요소 덩어리를 더하거나 뺄 수 있기 때문에 DOM 수정이 쉬워진다.

✅ 이벤트 위임의 작동

이벤트 위임은 다음과 같이 작동합니다.

  • 컨테이너에 하나의 핸들러를 할당합니다.
  • 핸들러의 event.target을 사용해 이벤트가 발생한 요소가 어디인지 알아냅니다.
  • 원하는 요소에서 이벤트가 발생했다고 확인되면 이벤트를 핸들링합니다.

😈 레퍼런스

profile
어제보다 오늘 더 노력하는 프론트엔드 개발자

0개의 댓글