이벤트 위임(bubbling & capturing), event.target

JY·2021년 3월 15일
0

0. 표준 DOM 이벤트에서 정의한 이벤트 흐름의 3가지 단계

  • 캡처링 단계 – 이벤트가 하위 요소로 전파되는 단계
  • 타깃 단계 – 이벤트가 실제 타깃 요소에 전달되는 단계
  • 버블링 단계 – 이벤트가 상위 요소로 전파되는 단계

    <td>를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파되고(캡처링 단계), 이벤트가 타깃 요소에 도착해 실행된 후(타깃 단계), 다시 위로 전파됩니다(버블링 단계). 이런 과정을 통해 요소에 할당된 이벤트 핸들러가 호출됩니다.



1. 이벤트 버블링(event bubbling)

1) 버블링이란?

한 요소에 이벤트가 발생하면,그 요소에 할당된 핸들러가 동작하고, 이어서 상위 요소로 올라가며 각 요소에 할당된 이벤트핸들러를 차례로 실행하는 것.

  • 최상단의 조상 요소를 만날 때까지 반복하며 각 요소에 할당된 핸들러가 동작한다.

2) 흐름

  • child -> parent -> grandparent ->...

3) event.target & event.currentTarget(=this)

부모 요소의 핸들러는 이벤트가 정확히 어디서 발생했는지 등에 대한 상세정보를 얻을 수 있다.
이벤트가 발생한 가장 안쪽의 요소는 타깃(target) 요소라고 불리고, event.target을 이용해 접근할 수 있다.

  • event.target : 실제로 이벤트가 발생한 요소를 가리킨다. 버블링이 진행되어도 변하지 않는다.

  • this(= event.currentTarget) : 현재 실행 중인 이벤트가 할당되어 있는 요소를 가리킨다.

  • 예시

<form onclick="alert('form')">
  <div>
    <p></p>
  </div>
</form>
// <p>클릭 -> event.target: <p>, this: <form>
// <div>클릭 -> event.target: <div>, this: <form>
// <form>클릭 -> event.target: <form>, this: <form>

4) 활용

  • Q : for문에서 동적으로 button을 만들고 그 button에 이벤트 리스너를 걸어야할 때, for문 안에서 이벤트 리스너를 거는 방법 이외에 다른 방법이 있을까요?
    => 여러 요소에 동일한 이벤트핸들러를 걸고 싶을 때

  • A : button들을 감싸는 외부 요소(wrapper)를 만들고 외부 요소에 이벤트 리스너를 걸면 버블링이 일어나 button들을 눌렀을 때 해당 이벤트가 발생한다.



2. 이벤트 캡쳐링(event capturing)

1)캡쳐링이란?

버블링과 반대방향으로 이벤트가 전달. 상위노드부터 하위노드로 이벤트가 전달되는 형태.

  • addEventListener(이벤트이름, 이벤트핸들러, true)
box.addEventListener("click", function (ev) {
  alert("click Box");
}, true);  // true를 주면 캡쳐링을 사용하겠다는 뜻
  • 캡처링에 관한 코드를 발견하는 일은 거의 없을 것...



3. 이벤트 위임(event delegation)

1) 캡쳐링은 상위요소에 등록된 이벤트부터! 버블링은 하위요소에 등록된 이벤트부터!

빨간박스 : A
파란박스 : B
노란박스 : C

//캡쳐링 이벤트 등록
A.addEventListener("click", function (ev) {
  alert("click A Capture");
}, true);
  
B.addEventListener("click", function (ev) {
  alert("click B Capture");
}, true);
  
C.addEventListener("click", function (ev) {
  alert("click C Capture");
}, true);

//버블링 이벤트 등록
A.addEventListener("click", function (ev) {
  alert("click A Bubbling");
});
  
B.addEventListener("click", function (ev) {
  alert("click B Bubbling");
});
  
C.addEventListener("click", function (ev) {
  alert("click C Bubbling");
});

노란박스(C) 클릭할 경우 알림 뜨는 순서

  • "click A Capturing"
  • "click B Capturing"
  • "click C Capturing"
  • "click C Bubbling"
  • "click B Bubbling"
  • "click A Bubbling"

2) ev.stopPropagation();

해당 이벤트까지만 생성하고 다음 이벤트부터는 중지한다.

예시1 - B에 등록

//캡쳐링 이벤트 등록
A.addEventListener("click", function (ev) {
  alert("click A Capture");
}, true);

B.addEventListener("click", function (ev) {
  ev.stopPropagation();       //stopPropagation 등록
  alert("click B Capture");
}, true);
  
C.addEventListener("click", function (ev) {
  alert("click C Capture");
}, true);

//버블링 이벤트 등록
A.addEventListener("click", function (ev) {
  alert("click A Bubbling");
});
  
B.addEventListener("click", function (ev) {
  alert("click B Bubbling");
});
  
C.addEventListener("click", function (ev) {
  alert("click C Bubbling");
});

노란박스(C) 클릭할 경우 알림 뜨는 순서

  • "click A Capturing"
  • "click B Capturing"
    (이후의 이벤트들은 발생하지 않음)

예시2 - C에 등록

//캡쳐링 이벤트 등록
A.addEventListener("click", function (ev) {
  alert("click A Capture");
}, true);

B.addEventListener("click", function (ev) {
  alert("click B Capture");
}, true);
  
C.addEventListener("click", function (ev) {
  ev.stopPropagation();       //stopPropagation 등록
  alert("click C Capture");
}, true);

//버블링 이벤트 등록
A.addEventListener("click", function (ev) {
  alert("click A Bubbling");
});
  
B.addEventListener("click", function (ev) {
  alert("click B Bubbling");
});
  
C.addEventListener("click", function (ev) {
  alert("click C Bubbling");
});

노란박스(C) 클릭할 경우 알림 뜨는 순서

  • "click A Capturing"
  • "click B Capturing"
  • "click C Capturing"
  • "click C Bubbling"
    (같은 요소에 등록된 이벤트까지는 모두 처리해준다. 따라서 C Bubbling까지는 실행됨.)

3) ev.preventDefault();

현재 발생한 이벤트의 디폴트 기능들을 막음.
ex) <a>tag 는 저장된 링크로 이동하는 기능이 있는데 이 기능이 작동하지 않도록 함.

0개의 댓글