event Bubbling and capturing

강정우·2023년 3월 15일
0

JavaScript

목록 보기
34/53
post-thumbnail
  • 우선 이벤트 버블링과 캡쳐링에 대해 간략히 설명하자면 사용자가 어떠한 요소를 클릭했을 때 DOM 트리의 상위요소까지 모두 눌리는 것이 Bubbling 하위소요까지 모두 눌리는 것이 Capturing이다.

event Bubbling

  • 그러다면 예제로 밑에 코드를 살펴보자
<style>
  form * {
  	margin: 10px;
    border: 1px solid blue;
  }
</style>

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>
FORM
DIV

P

  • 위 코드를 실행하면 3번의 alert이 뜬다.

  • 최하위 요소부터 p=>div=>form 이렇게. 이 프로세스를 바로 "Bubbling" 이라고 한다.
    이벤트가 거품처럼 밑에서부터 부모를 향해 "bubble"하기 때문이다.

이때 "거의" 모든 이벤트가 버블링 된다는 것이다. 여기서 포인트는 "almost" 이다.
예를 들어 focus이벤트는 버블링되지 않는다. 하지만 이는 예외이고 대부분이 버블링된다.

event.target

  • 버블링을 막을 수 있는 방법으로 부모=>자식일 때 .target 속성을 통하여 실제로 발생한 위치에 대한 세보 정보를 얻을 수 있다.

this VS event.target

  • event.target : 이벤트를 시작한 "대상" 요소이며 버블링 프로세스를 통해 변경되지 않는다.
  • this : 현재 실행 중인 핸들러가 있는 "현재" 요소이다.

즉, 위의 코드에서 보면 this값은 계속하여 form tagName이 나오고 event.target의 tagName은 해당 요소가 정확히 찍혀 나온다.

  • 이벤트 버블링의 특성상 보통 <html> 를 거처 document Obj까지 버블링하며 어떠한 이벤트들은 window 객체까지 올라가기도 한다.
    그렇다면 다른 방법으로 버블링을 막을 순 없을까?

stopPropagation

  • propagation 사전적 의미로 "전파"를 뜻하며 사용법은 event.stopPropagation이다.
<body onclick="alert(`the bubbling doesn't reach here`)">
  <button onclick="event.stopPropagation()">Click me</button>
</body>
  • 즉 클릭한 요소만 발생시키고 나머지 이벤트들은 버블링하는 것을 막는다.

event.stopImmediatePropagation()

  • 요소에 단일 이벤트에 대한 여러 이벤트 핸들러가 있는 경우
    그 중 하나가 버블링을 중지하더라도 다른 하나는 계속 실행된다.

  • 즉, event.stopPropagation()를 써도 해당 이벤트 핸들러만 멈추고 다른 이벤트들은 계속하여 버블링이 진행되지만 event.stopImmediatePropagation() 를 사용하면 이 이후에 핸들러들은 실행되지 않는다.

하지만 우리가 일반적으로는 bubbling을 막을 필요는 거의 없을 것이다.
예를 들어 우리가 nested menu를 만들었다 치자. 각 요소마다. stopPropagation을 걸어버리면 메뉴는 어디상 작동하지 않는다.
또한 분석을 위하여 window에 사용자 "클릭" 이벤트를 걸어둬도 그것역시 작동하지 않게 된다.

event Capturing

  • 작동 방법은 똑같지만 1가지 다른점이 있다. 바로 JS단에서 addEventListener API에 capture 속성값만 추가해주면 된다.
<body>
	<div class="one">
		<div class="two">
			<div class="three">
			</div>
		</div>
	</div>
</body>
var divs = document.querySelectorAll('div');

divs.forEach(function(div) {
	div.addEventListener('click', logEvent, {
		capture: true // default 값은 false
	});
});

function logEvent(event) {
	console.log(event.currentTarget.className);
}
  • 만약에 위 코드를 코드펜에서 실행하면 one=>two=>tree 순으로 나온다.

event Delegation

  • 사실상 위 개념들을 delegation을 위해 배웠다고해도 무방하다.

  • delegation은 사전적 의미로 위임을 뜻한다.
    즉, 하위요소에서 이벤트 설정을 일일이 하지 않고 상위요소로 "위임"한다. 이렇게 되면 이벤트 설정을 상위요소에서 1번만 해주면 된다는 아주 미친 효율을 갖고있다.

<h1>오늘의 할 일</h1>
<ul class="itemList">
	<li>
		<input type="checkbox" id="item1">
		<label for="item1">이벤트 버블링 학습</label>
	</li>
	<li>
		<input type="checkbox" id="item2">
		<label for="item2">이벤트 캡쳐 학습</label>
	</li>
</ul>
// 하위요소에 일일이 이벤트를 넣어주는 코드
var inputs = document.querySelectorAll('input');
inputs.forEach(function(input) {
	input.addEventListener('click', function(event) {
		alert('clicked');
	});
});

// 새 리스트 아이템을 추가하는 코드
var itemList = document.querySelector('.itemList');
var li = document.createElement('li');
var input = document.createElement('input');
var label = document.createElement('label');
var labelText = document.createTextNode('이벤트 위임 학습');

input.setAttribute('type', 'checkbox');
input.setAttribute('id', 'item3');
label.setAttribute('for', 'item3');
label.appendChild(labelText);
li.appendChild(input);
li.appendChild(label);
itemList.appendChild(li);

// delegation을 통하여 상위요소에 1번 이벤트를 넣어주는 코드
var itemList = document.querySelector('.itemList');
itemList.addEventListener('click', function(event) {
	alert('clicked');
});

reference

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글