[JS] 이벤트 버블링, 이벤트 캡처링, 이벤트 위임

Junyeong Kim·2024년 1월 25일
0

개발

목록 보기
6/16
post-thumbnail

JS에서 이벤트를 걸어주면, 브라우저에서 어떤 동작을 했을 때 이벤트를 감지하고 이벤트를 발생시킨다.
브라우저에서 이벤트를 감지하는 방식인 이벤트 버블링, 이벤트 캡쳐링, 이벤트 위임을 정리해보고자 한다.

이벤트 버블링 Event Bubbling

이벤트 버블링하위 요소에서 발생한 이벤트가 상위 요소로 전파되는 방식을 말한다.
아래에서 부터 위로 거품이 뽀글뽀글하는 것과 같다고 하여 Bubbling이라는 이름이 붙었다.

  <div class="grandma">
    <div class="mother">
      <div class="daughter">
        이벤트 버블링
      </div>
    </div>
  </div>

위와 같은 DOM 구조에서 세 div태그에 모두 이벤트를 걸어보겠다.

grandma.addEventListener("click", function(e){console.log(e.currentTarget.className)});

mother.addEventListener("click", function(e){console.log(e.currentTarget.className)});

daughter.addEventListener("click", function(e){console.log(e.currentTarget.className)});

+ 위 코드에서 e.target이 아닌 e.currentTarget을 써준 이유 : 이벤트가 전파될 때 비록 상위 요소들의 이벤트가 동작하기는 하지만, 타겟 이벤트 객체는 이벤트 전파가 시작된 요소(daughter)이기 때문에, target은 daughter를 가리킨다. 그래서 e.currentTarget 대신 e.target을 써주게 되면 daughter가 세 번 출력된다.

그리고 이벤트버블링 글자를 클릭하면, 아래와 같은 결과를 볼 수 있다.

'딸' 클래스를 가진 글자를 눌러주었더니, 차례로 엄마, 할머니 div에 걸린 이벤트들이 동작했다.
이와 같이 하위 요소에서 상위 요소로 이벤트가 전파되는 것이 이벤트 버블링이다.

이벤트 캡처링 Event Capturing

이벤트 캡처링은 이벤트 버블링과 반대이다. 상위 요소의 이벤트가 하위 요소로 전파되는 방식이다.
특정 이벤트가 발생했을 때, body(최상위 요소)에서 부터 이벤트가 발생된 요소를 찾아 내려간다.

  <div class="grandma">
    <div class="mother">
      <div class="daughter">
        이벤트 캡처링
      </div>
    </div>
  </div>

동일하게 DOM 구조를 만들어준다. 이벤트 버블링과 달라지는 점은 addEventListener를 걸어줄 때이다.

grandma.addEventListener("click", function(e){console.log(e.currentTarget.className)}, {capture:true});

mother.addEventListener("click", function(e){console.log(e.currentTarget.className)}, {capture:true});

daughter.addEventListener("click", function(e){console.log(e.currentTarget.className)}, {capture:true});

동일하게 이벤트를 걸어주고, 세번째 아규먼트로 {capture:true}를 추가해주면 이벤트 캡처링을 구현할 수 있다. 이제 이벤트캡처링 글자를 클릭하면 아래와 같은 결과를 볼 수 있다.


이벤트 버블링에서 딸 > 엄마 > 할머니 순으로 이벤트가 전파된 것과 반대로 할머니 > 엄마 > 딸 순으로
상위 요소에서 하위 요소로 이벤트가 전파되는 방식을 볼 수 있고, 이것이 이벤트 캡처링이다.

이벤트 전파의 방지?

내가 선택한 요소 그 자체에만 이벤트를 주고 싶을 때(ex: 어떤 버튼을 클릭했을 때, 해당 버튼 상위의 버튼들은 동작하지 않도록 한다거나.. 등등), 위와 같은 이벤트 전파 방식을 막을 수 있는 방법도 있다.

event.stopPropagation()

이벤트를 걸어주고자 하는 요소의 콜백 함수 내에 event 객체의 stopPropagation 메서드를 사용하게 되면 이벤트 전파를 막아준다.

  <div class="grandma">
    <div class="mother">
      <div class="daughter">
        stopPropagation
      </div>
    </div>
  </div>
function printClassName(e){
  e.stopPropagation();
  console.log(e.currentTarget.className);
}

grandma.addEventListener("click", printClassName);

mother.addEventListener("click", printClassName);

daughter.addEventListener("click", printClassName);

각 div들에 걸어줄 이벤트 동작 함수에 stopPropagation을 사용하여 이벤트 전파를 막아준 후 stopPropagation 글자를 클릭하면, 아래와 같은 결과가 나타난다.


해당 글자가 소속되어 있는 딸 div의 이벤트만 동작하였다.

하지만 이러한 이벤트 전파는 훨씬 더 많은 경우에 유용하게 쓰일 수 있기 때문에 가급적 막지 않는 것이 좋다.
이벤트 전파를 유용하게 쓸 수 있는 예시 중 하나는 이벤트 위임이라는 코딩 방식이다.

이벤트 위임

<div class="family">
    <span class="grandma">
      <span class="mother">
        <span class="daughter">
          이벤트 위임
        </span>
      </span>
    </span>
  </div>

이러한 DOM 구조에서 아래와 같이 요소 하나하나에 일일이 이벤트를 걸어주었다고 해보자.

const spans = document.querySelectorAll('span');

spans.forEach(function(span) {
	span.addEventListener('click', function(e) {
		console.log(e.currentTarget.className);
	});
});

일단은 잘 동작할 것이다. 하지만 이렇게 이벤트를 걸어준 이후 시점에서 딸 div의 자식 요소 daugters_son을 추가한다면?

const spans = document.querySelectorAll('span');
const family = document.querySelector('.family');

spans.forEach(function(span) {
	span.addEventListener('click', function(e) {
		alert('clicked');
	});
});
______________________________________________________

const span = document.createElement('span');
span.classList.add('daugters_son');
span.textContent = "증조손자";
family.appendChild(span);

당연히 새로 생긴 daugters_son div에는 이벤트가 걸려있지 않을 것이다.
그래서 증조손자 글자를 눌렀을 때는 아무런 동작도 일어나지 않는다. (깨알 같이 손녀 글자에 일어나는 이벤트 버블링)

그렇다면 새롭게 자식 요소가 생길 때마다 일일이 이벤트를 걸어줘야할까?? 그건 너무 귀찮다..
이럴 때 사용하는 것이 이벤트 위임이다.

이벤트 위임이란 이벤트를 일일이 거는 것이 아니라, 상위 요소에서 한번에 하위 요소들의 이벤트를 제어하는 방식이다.

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

const family = document.querySelector('.family');

family.addEventListener('click', function(e) {
	alert('clicked');
});

기존의 코드는 span인 태그들을 몽땅 가져와서 각각에 걸어줬다면, 이벤트 위임 방식의 코드는 이벤트를 걸어주고 싶은 span들을 품고 있는 가장 상위의 요소인 family div를 가져와서, 거기에 이벤트를 걸어주었다.

이제는 아래와 같이 손녀를 누르던, 증조손자를 누르던 이벤트가 동작한다.

이런 식으로 이벤트 위임 방식을 사용하게 되면 이벤트 전파로 인해 아무리 이후 시점에서 새로운 하위 요소들을 추가해주어도 자동적으로 family 하위의 모든 요소들에 이벤트가 걸리게 된다.

정보 출처

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

profile
개발자가 되고싶어?? 네

0개의 댓글

관련 채용 정보