표준 DOM 이벤트에서 정의한 이벤트 흐름엔 3가지 단계가 있다.
- 캡처링 단계 - 이벤트가 하위 요소로 전파되는 단계
- 타깃 단계 - 이벤트가 실제 타깃 요소에 전달되는 단계
- 버블링 단계 - 이벤트가 상위 요소로 전파되는 단계
버블링
<div onclick="console.log('div 핸들러!')">
<button>클릭!</button>
</div>
//버튼을 클릭하면 div에 할당한핸들러가 동작!
why? div에 할당한 핸들러가 동작하는가?
버블링
버블링의 원리
- 한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다.
- 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작하게된다.
<form onclick="console.log('form')">FORM
<div onclick="console.log('div')">DIV
<p onclick="console.log('p')">P</p>
</div>
</form>
//P 클릭시, p -> div -> form 순으로 출력
//div 클릭시 div -> form 순으로 출력
// form 클릭시 for 출력
- 이러한 흐름을 이벤트 버블링 이라고 부른다.
event.target
- 부모 요소의 핸들러는 이벤트가 정확히 어디서 발생했는지 등에 대한 자세한 정보를 얻을 수 있다.
- 이벤트가 발생한 가장 안쪽의 요소는 **타깃(target)**요소 라고 불리고, **event.target**을 사용해 접근할 수 있다.
- event.target과 this(=event.currentTarget)의 차이점?
- event.target은 실제 이벤트가 시작된 ‘타깃’요소 이다. 버블링이 진행되도 변하지않는다.
- this는 ‘현재’요소로, 현재 실행 중인 핸들러가 할당된 요소를 참조한다.
<form id=form>
<div>DIV
<p>P</p>
</div>
</form>
form.onclick = function(event) {
console.log(`target = ${event.target.tagName} this=${this.tagName}`)
}
//P클릭시, target = P, this = FORM
//DIV클릭시, target = DIV, this = FORM
//FORM클릭시, target = FORM, this = FORM
버블링 중단하기
- 이벤트 버블링은 타깃 이벤트에서 시작해서, <html>요소를 거쳐, document 객체를 만날 때까지 각 노드에서 모두 발생한다.
- 몇몇 이벤트는 window 객체가지 거슬러 올라가기도 한다. 이때도 모든 핸들러가 호출된다.
- 그런데, 핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령할 수도 있다.
- event.stopPropagation() ****이벤트 객체 메소드를 사용하면 된다.
<body onclick="console.log(`body!)">
<button onclick="event.stopPropagation()">클릭.</button>
</body>
//버튼 클릭! = 반응없음
// body영역 클릭! = body!
event.stopImmediatePropagation() ?
- 한 요소의 특정 이벤트를 처리하는 핸들러가 여러개인 상황에서, 핸들러 중 하나가 버블링을 멈추더라도 나머지는 여전히 동작한다.
- event.stopPropagation()은 위쪽으로 일어나는 버블링은 막아주지만, 다른 핸들러들이 동작하는 건 막지 못한다.
- 버블링을 멈추고, 요소에 할당된 다른 핸들러의 동작도 막으려면event.stopImmediatePropagation()을 사용해야 한다. 이 메서드를 사용하면 요소에 할당된 특정 이벤트를 처리하는 핸들러 모두가 동작하지 않는다.
캡처링
캡처링의 원리
- 한 요소에 이벤트가 발생하면,부모 요소의 핸들러가 동작하고 점차 아래로 전파된다.
- 가장 최하단의 자식 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작하게된다.
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => console.log(`캡쳐링: ${elem.tagName}`), true);
elem.addEventListener("click", e => console.log(`버블링: ${elem.tagName}`));
}
/*
p클릭시 :html -> body -> form -> div -> p -> p -> div -> form -> body -> html
*/
- html→ body → from → div : 캡처링 단계
- p : 타깃 단계
- div → form → body → html : 버블링 단계