# 버블링과 캡쳐링
# 버블링 Bubbling
- 한 요소에 이벤트 발생 → 요소에 할당된 핸들러 동작
- 부모 요소의 핸들러 동작
- 가장 최상단의 조상요소를 만날 때 까지 반복되면서 요소 각각에 할당된 핸들러 동작.
- 타깃 이벤트에서 시작해서
<html>
요소를 거쳐document
객체를 만날 때까지 각 노드에서 모두 발생함. window
객체까지 거슬러 올라가는 이벤트도 존재함.
<style>
body * {
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>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 가장 안쪽의
<p>
를 클릭 <p>
에 할당된onclick
핸들러 동작- 바깥의
<div>
에 할당된 핸들러 동작 - 그 바깥의
<form>
에 할당된 핸들러 동작 document
객체를 만날 때까지, 각 요소에 할당된onclick
핸들러 동작
거의 모든 이벤트는 버블링 된다.
focus
이벤트는 버블링 되지 않는다.
# target
# event.target
- 실제 이벤트가 시작된 ‘타깃’ 요소
- 버블링이 진행되어도 변하지 않음
# event.currentTarget(this)
현재 요소
현재 실행중인 핸들러가 할당된 요소를 참조
event.target
– form 안쪽에 실제 클릭한 요소를 가리킴event.currentTarget
- form
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="example.css">
</head>
<body>
<form id="form">FORM
<div>DIV
<p>P</p>
</div>
</form>
</body>
<script>
form.onclick = function(event) {
event.target.style.backgroundColor = 'yellow';
// chrome needs some time to paint yellow
setTimeout(() => {
alert("target = " + event.target.tagName + ", this=" + this.tagName);
event.target.style.backgroundColor = ''
}, 0);
};
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# event.stopPropagation 버블링 중단하기
<body onclick="alert(`버블링은 여기까지 도달하지 못합니다.`)">
<button onclick="event.stopPropagation()">클릭해 주세요.</button>
</body>
1
2
3
2
3
# event.stopImmediatePropagation()
한 요소의 핸들러가 여러 개일 때,
event.stopPropagation()
으로 핸들러 중 하나가 버블링을 멈추더라도, 나머지 핸들러는 여전히 동작한다.
다른 핸들러의 동작도 막으려면
event.stopImmediatePropagation()
사용- 요소에 할당된 핸들러 모두가 동작하지 않음
# 꼭 필요한 경우를 제외하곤 버블링을 막지 마세요!
버블링은 유용합니다. 아키텍처를 잘 고려해 진짜 막아야 하는 상황에서만 버블링을 막으세요.
event.stopPropagation()
→ 추후에 문제 유발 가능
이벤트 버블링을 막아야 하는 경우는 거의 없습니다. 버블링을 막아야 해결되는 문제라면 커스텀 이벤트 등을 사용해 문제를 해결할 수 있습니다.
# 행동 패턴 분석
사람들이 페이지에서 어디를 클릭했는지 등의 행동 패턴을 분석하기 위해, window 내에서 발생하는 클릭 이벤트 전부를 감지하기로 결정합니다.
클릭 이벤트를 감지하기 위해 document.addEventListener('click'…)을 사용합니다.
stopPropagation 로 버블링을 막아놓은 영역에선 분석 시스템의 코드가 동작하지 않기 때문에, 분석이 제대로 되지 않습니다.
안타깝게도 stopPropagation을 사용한 영역은 '죽은 영역(dead zone)'이 되어버립니다.
# 이벤트의 흐름
- 캡처링 단계 – 이벤트가 하위 요소로 전파되는 단계
- 타깃 단계 – 이벤트가 실제 타깃 요소에 전달되는 단계
- 버블링 단계 – 이벤트가 상위 요소로 전파되는 단계
캡처링과 버블링 단계의 핸들러는 타깃 단계에서 트리거됩니다.
# 캡처링
이벤트 캡처링 단계를 이용하는 경우는 흔치 않다.
elem.addEventListener(..., {capture: true})
elem.addEventListener(..., true)
1
2
2
capture 옵션
- false(default 값) 핸들러는 버블링 단계에서 동작합니다.
- true 핸들러는 캡처링 단계에서 동작합니다.
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
<script>
for(let elem of document.querySelectorAll('*')) {
elem.addEventListener("click", e => alert(`캡쳐링: ${elem.tagName}`), true);
elem.addEventListener("click", e => alert(`버블링: ${elem.tagName}`));
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
<p>
를 클릭하면 다음과 같은 순서로 이벤트가 전달됩니다.
- HTML → BODY → FORM → DIV (캡처링 단계, 첫 번째 리스너)
- P (타깃 단계, 캡쳐링과 버블링 둘 다에 리스너를 설정했기 때문에 두 번 호출됩니다.)
- DIV → FORM → BODY → HTML (버블링 단계, 두 번째 리스너)
# event.eventPhase
현재 발생 중인 이벤트 흐름의 단계를 알 수 있음 (캡처링=1, 타깃=2, 버블링=3)
# Reference
- https://ko.javascript.info/bubbling-and-capturing