이벤트 버블링/캡처링, 그리고 이벤트 위임

최성2·2022년 6월 3일
1
post-thumbnail

캡처링: window로부터 이벤트가 발생한 요소까지 이벤트를 전파한다
버블링: 이벤트가 발생한 요소부터 window까지 이벤트를 전파한다


1. 이벤트 버블링(Event Bubbling)

특정 요소에서 이벤트가 발생했을 때 해당 이벤트가 상위 요소들로 전달되어 가는 특성을 의미한다

<div class="one">
	one
	<div class="two">
		two
		<div class="three">
			three
		</div>
	</div>
</div>
const div = document.querySelectorAll('div')

div.forEach((el) => {
  el.addEventListener('click', bubbling)
})

function bubbling(e) {
  console.log(e.currentTarget.className);
}

최하위 태그인 three를 클릭하면, 3개의 이벤트가 모두 발생된다. 그 이유는 브라우저가 이벤트를 감지하는 방식 때문이다

✅ 특정 요소에서 이벤트가 발생했을 때 그 이벤트를 최상위에 있는 요소까지 전파시킨다

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

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

거의 모든 이벤트는 버블링 된다
⇒ focus 와 같이 버블링되지 않는 이벤트도 있지만, 대부분 버블링된다

event.target vs event.currentTarget(=this)

event.target은 실제 이벤트가 시작되는 요소다. 버블링이 진행되어도 변하지 않는다

event.currentTarget은 현재 요소로, 실행 중인 핸들러가 할당된 요소를 참조한다


2. 이벤트 캡처(Event Capture)

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

버블링이 전달의 느낌이라면 캡처는 탐색의 느낌이다

*이벤트 버블링을 반대 방향으로 탐색하고 싶을 때

: 이벤트 리스너를 등록할 때 capture: true를 설정해주면 된다

const div = document.querySelectorAll('div')

div.forEach((el) => {
  el.addEventListener('click', bubbling, {
    capture: true // default - false // capture라고 명시해주지 않고 true만 써도 무방
  })
})

function bubbling(e) {
  console.log(e.currentTarget.className);
}


3. event.stopPropagation()

이벤트가 전파되는 것을 막는 메서드

const div = document.querySelectorAll('div')

div.forEach((el) => {
  el.addEventListener('click', bubbling)
})

function bubbling(e) {
	e.stopPropagation() 👈
  console.log(e.currentTarget.className);
}

이벤트 버블링: 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하는 것을 방해한다

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

*버블링을 막으면 추후 문제가 될 수 있기 때문에, 꼭 필요한 경우를 제외하고는 버블링을 막지 말자

하나의 이벤트를 처리하는 핸들러가 여러개인 상황에서, event.stopPropagation()은 위쪽으로 일어나는 버블링은 막아주지만, 다른 핸들러들이 동작하는 건 막지 못한다
버블링을 멈추고, 요소에 할당된 다른 핸들러의 동작도 막으려면 event.stopImmediatePropagation()을 사용해야 한다


4. 이벤트 위임(Event Delegation)

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

<ul id="parent-list">
	<li id="item1">Item 1</li>
	<li id="item2">Item 2</li>
	<li id="item3">Item 3</li>
</ul>
document.getElementById("todo-list").addEventListener("click", function (e) {            
   if (e.target && e.target.nodeName == "LI") {                
      console.log(`List item  ${e.target.id} was clicked!`);
    }
 });
<ul id="todo-list">
	<li class="todo-item">
      <span class="item-name">${listInputValue}</span>
      <div class="item-btn">
        <button type="button" class="done-btn">완료</button>
        <button type="button" class="edit-btn">수정</button>
        <button type="button" class="remove-btn">삭제</button>
      </div>
    </li>
</ul>
const toDoList = document.querySelector('#todo-list')

toDoList.addEventListener("click", (e) => {
  if(e.target.classList.contains("edit-btn")) {
    editToDoList(e)
  }

  if(e.target.classList.contains("remove-btn")) {
    removeToDoList(e)
  }
})

장점

  1. 동적으로 추가되는 요소들에도 동작한다
  2. 간결한 코드
profile
hello!

0개의 댓글