🧼Event Bubbling

p만 클릭했는데 확인 누르면 div➡form 까지 알림창 뜨는걸 볼 수 있음🧼

중첩된 요소에서 이벤트 발생하면 👩‍🦱부모로 계속 전달 되는 것

const form = document.querySelector('form'); // form 태그 선택
const div = document.querySelector('div');   // div 태그 선택
const p = document.querySelector('p');       // p 태그 선택

// form 요소에 클릭 이벤트 핸들러 설정
form.onclick = function (event) {
  // 사용자가 실제 클릭한 요소의 배경색을 노란색으로 변경
  event.target.style.backgroundColor = 'yellow';

  // 1초 뒤에 아래 코드 실행
  setTimeout(() => {
    // 클릭된 요소와 이벤트 리스너가 설정된 요소의 태그명을 alert로 표시
    alert('target = ' + event.target.tagName + ", this = " + this.tagName);
    
    // 배경색 원래대로 돌리기
    event.target.style.backgroundColor = '';
  }, 1000);
}

현재 Div를 클릭했으니
event.target은 실제 이벤트가 시작된 타깃인 DIV(TagName)

event.currentTarget은 현재 실행 중인 핸들러가 할당된 요소를 참조➡FORM

🧼버블링 중단하는 법 - event.stopPropagation( )✋


📸Event Capturing

이벤트 버블링관 달리 제일 상단에 있는 요소에서 아래로 이벤트 내려오는 것

이벤트의 3단계 흐름을 알면
왜 캡처링은 내려오고, 버블링은 올라가는지 정확히 이해할 수 있음👏

🧐이벤트의 3단계 흐름

버블링 & 캡처링의 이벤트 흐름을 알기위한 예제

body * {
	margin: 10px;
    border: 1px solid red;
}
<form>
  Form
  <div>
    Div
    <p>P</p>
  </div>
</form>
// * : 모든 요소 즉, form div p 모두 선택하기 위한 거
// element 안에 모든 요소들이 들어있을것
// of : 배열 || 유사 배열의 값을 하나씩 꺼낼 때 씀
for (let element of document.querySelectorAll('*')) {
  element.addEventListener('click', event => alert(`캡처링: ${element.tagName}`), {capture: true});
  element.addEventListener('click', event => alert(`버블링: ${element.tagName}`));
}

💬 옵션 - capture: true
👉 "버블링보다 먼저 실행해줘!"라고 브라우저한테 말하는 거❗

P를 클릭했는데 먼저 캡처링: HTML 알림창이 뜨는 걸 볼 수 있음

캡처링 순서대로 다 된 후에 선택한 요소 P

아까 버블링만 봤을 땐 아래서 위로만 간다고 생각했었는데
그건 캡처링 부분을 아예 무시했어서 그랬던거임✔

근데 이번엔 캡처링 부분까지 보여줄 수 있게
옵션 capture: true 넣고 모든 요소한테 이벤트 리스너를 등록 해보니까
실제로 window 부터 아래로 내려오고 그다음 타깃에 도달하고
타깃에 도달하면 다시 버블링으로 위로 올라가는 걸 볼 수 있음👀

🤓정리

  • 이벤트➡마우스 클릭과 같이 웹 브라우저에서 발생하는 동작
  • 이벤트 흐름 : 이벤트 버블링 & 캡처링 두가지 기본 모델이 있음
  • 이벤트 등록 = addEventListener( )로 리스너 연결
  • 이벤트 객체➡이벤트 리스너 내에서만 접근 가능
  • preventDefault( ) 메서드 사용하여 이벤트의 기본 동작을 방지하지만
    이벤트 흐름을 중지하진 않음
  • stopPropagation( ) 메서드 사용하여 DOM 트리를 통한
    이벤트 흐름을 중지하지만 브라우저 기본 동작을 취소하진 않음

🎭Event Delegation - 이벤트 위임( 하위➡상위요소 )

이벤트를 👩‍🦱부모 요소에 한 번만 등록하고
이벤트 버블링 이용해서 👩‍자식 요소의 이벤트까지 처리하는 방식🔥

<div id="buttons">
  <button class="buttonClass">
    Click me
  </button>
  
  <button class="buttonClass">
    Click me
  </button>
</div>
// ✅ getElementsByClassName( )은 '유사 배열'(HTMLCollection)
// 이건 HTMLCollection을 반환, 배열처럼 보이지만 진짜 배열은 아님🙅‍♀️
// 근데 이 HTMLCollection은 이터러블(iterable)이기 때문 → for...of로 순회 가능
const buttons = document.getElementsByClassName("buttonClass");

for (const button of buttons) {
  	button.addEventListener('click', () => alert('clicked'));
}

각각 버튼 클릭하면 똑같은 알림창이 나올거임

개발하다 보면 갑자기 JS 이용해서 버튼 하나를 더 추가해야 될 때도 있을거임

// 버튼을 <div id="buttons"> 이 안에 생성할거니까 가져옴
let buttonList = document.querySelector('#buttons');
// 버튼 생성하기
let button = document.createElement('button');

// setAttribute : HTML 요소에 속성(attribute) 추가하거나 수정하는 메서드
button.setAttribute("class", "buttonClass");
button.innerText = 'Click me';

// 이렇게만 하면 화면에 보이진 않고 생성만 해둔것
// 화면에 보이게 하려면 생성한걸 어딘가에 append 해줘야함
buttonList.appendChild(button);

JS로 버튼 하나 더 추가 완료✨

😤 문제 상황 정리
버튼을 자바스크립트로 새로 만들면
그 버튼엔 이벤트 리스너가 안 붙어있음 → 클릭 동작❌
👉 그래서 매번 새로 만든 요소에 리스너를 따로 또 등록해야 해...🤯

💡 이럴 때 등장하는 해결책이 바로 이벤트 위임( Event Delegation ) 〰🐱‍🏍

// 이벤트 컨트롤 하는 부분을 자식들 buttonClass가 아닌 부모인 buttons가 할 수 있게 할 것
const buttons = document.getElementById('buttons');
buttons.addEventListener('click', () => alert('clicked'));

/* ☝ const buttons = document.getElementsByClassName("buttonClass");

for (const button of buttons) {
  	button.addEventListener('click', () => alert('clicked'));
} */

let buttonList = document.querySelector('#buttons');
let button = document.createElement('button');

button.setAttribute("class", "buttonClass");
button.innerText = 'Click me';
buttonList.appendChild(button);

이젠 위임이 돼서 세번째 버튼도 잘 작동하는 걸 볼 수 있음🥳

부모(buttons)에 등록된 이벤트 리스너➡이벤트 버블링 덕분에
자식 버튼이 나중에 만들어져도 클릭 이벤트가 부모까지 전달되기 때문🙆‍♀️

✅ 이벤트 위임은 이벤트가 위로 올라가는 버블링을 이용해서
나중에 생긴 자식도 처리할 수 있음👏

profile
안녕하세요! 퍼블리싱 & 프론트엔드 개발 공부 블로그 입니다!

0개의 댓글