html>body>button 의 부모자식관계를 가지는 html구조가 있다고 치자.
<html>
<body>
<button>눌러주세요</button>
</body>
</html>
각 요소들에 click event를 만들고, button을 click하면 어떻게 될까?
우리는 입장에서는 어려울 것 없이 button이 먼저 실행된다고 생각하지만 아니다.
엄밀히 말해 우리는 html안에있는 body안에있는 button을 누른것이기 때문에 브라우저 입장에서는 이 세가지중 정확히 무엇을 누른건지 애매하다고 생각하기 때문이다.
그래서 button에 클릭이벤트가 일어났을 때, 만약 부모요소에도 이벤트가 적용되어 있다면 이 경우 브라우저는 전부 다 실행을 시켜버린다. (body와 html에 클릭이벤트가 없으면 아무상관없는 이야기)
어떤 순서로? 아래 event flow 순서로!
[이벤트가 실행되면 브라우저는 무조건 이 Phase 대로 흘러가며 이벤트를 실행시킨다.]

propagate = 전파하다, 번식시키다
Capture phase
이벤트 캡쳐링(Event Capturing): 요소를 기준으로 가장 상위 부모요소부터(가장 구체성이 낮음) 자식인 target까지(가장 구체적인)의 순서로 간다. 이것을 propagate up이라고 한다. 가장 구체성이 높은곳까지 전파하러 올라간다는 느낌으로 이해하면 된다.
Target phase
이벤트 타겟(Event Target): 이벤트 당사자가 실행이 된다.
Bubble phase
이벤트 버블링(Event Bubbling): 이벤트 캡쳐링과 반대로 구체성이 가장 낮은 부모요소로 간다. 이것을 propagate down이라고 한다.
그런데 만약 위와같이 흘러간다면, 이벤트 캡쳐링단계에서도 만난 이벤트도 실행되고, 진짜 이벤트 타겟을 거쳐 다시 이벤트 버블링을 하며 내려가면서 또다시 이벤트들이 실행되기 때문에 아주 혼돈이 온다.
그래서 너, useCapture 할 거니?
그래서 브라우저는 이벤트 당사자가 아닌, 당사자와의 관계때문에 어쩔 수 없이 실행되는 요소들에게 event flow 순서 중
- 캡쳐단계에서 실행될건지
- 혹은 버블단계에서 실행될건지
를 선택할 수 있게 해준다.
그리고 기본적으로는 버블로 선택되어있는데, 우리가 평소에 의식하지 않고 실행하는 바로 그 순서이다.
이벤트 타겟이 실행된 이후에 버블링의 흐름에 따라 실행되게 된다. (button - body - html순)
말로하면 헷갈리니, 직접 해보자
html, body, button 모두에 클릭이벤트가 있는 경우이고, button을 누른다고 치자.
<html>
<body>
<button>눌러주세요</button>
</body>
</html>
<script>
const html = document.documentElement;
const body = document.body;
const btn = document.querySelector('button');
btn.addEventListener('click', function () {
console.log('버튼이다');
});
body.addEventListener('click', function () {
console.log('바디다');
});
html.addEventListener('click', function () {
console.log('html이다');
});
</script>
아래와 같은 단계로 실행된다.
Capture phase
먼저 캡쳐링 이벤트로 시작하지만 html과 body가 캡쳐를 사용하고있지 않으니
Target phase
이벤트 당사자인 버튼으로 넘어가 버튼이 처음으로 가장먼저 실행되고,
Bubble phase
버튼 다음 버블이벤트가 일어나며 body, html이 순서대로 실행되는 것이다.
캡쳐링 단계에서 html과 body가 실행되게 하고싶다면 어떻게할까?
프로젝트를 하다면 어떠한 이유로 이렇게 부모자식간에 이벤트가 여러개 중첩되어있을 때 어떤 이벤트를 먼저 실행시킬지 순서를 세밀하게 조작해야 하는 순간이 온다.
그때는 addEventListener의 3번째 인자값에 true값을 넣어주면 된다!
아래는 MDN에서 가녀온 인자값의 형태이다.
addEventListener(type, listener, useCapture);
3번째 인자값에 useCapture, 즉 캡쳐링이벤트를 쓸거니? 하고 묻는것에 true라고 대답해 준 것이다.
아무것도 넣지않을경우 자동으로 false가 들어간다(그래서 기본적으로 버블때 실행되는것이다)
아래 코드를 통해 살펴보자.
<script>
const html = document.documentElement;
const body = document.body;
const btn = document.querySelector('button');
btn.addEventListener('click', function () {
console.log('버튼이다');
});
body.addEventListener(
'click',
function () {
console.log('바디다');
},
true
);
html.addEventListener(
'click',
function () {
console.log('html이다');
},
true
);
</script>
브라우저님, 저는 제 의지가 아닌데 이벤트가 실행될 때 캡쳐단계에서 실행시켜주십쇼
라고 말하는것과 같다.
또 이 때 button에는 굳이 true값을 넣어줄 필요가 없는데, 자기자신이 이벤트의 당사자(target)이자 자식으로 또다른 요소가 있는것도 아니므로 자기자식때문에 뭔가 실행될 일이 애초에 없기 때문이다!
이제 버튼을 누르면,
Capture phase
캡쳐링이벤트로 시작하는데, 캡쳐를 사용한다고 한 html와 body를 만나니 순서대로 실행되고,
Target phase
이벤트 당사자인 버튼으로 넘어가 버튼이 실행된다.
Bubble phase
버튼 다음 버블이벤트가 일어나지만 현재 html, body, button모두 이미 실행됬으므로 아무것도 실행되지 않는다.

원래 버블로 기본셋팅이 되어있기 때문에 평소에 우리가 잘 활용하진 않지만,
나중에 프로젝트가 커지고 부모자식간의 요소에서 이벤트실행순서를 미세하게 컨트롤 해야할 때가 분명히 오고
또 면접 단골질문이라고 하니 잘 이해하고있다가 사용해야겠다.
'Front-End Developer > JavaScript' 카테고리의 다른 글
blur와 click을 같이 쓸 때, 내 맘대로 안되는 이유 (0) | 2022.05.13 |
---|---|
DOM이란 무엇인가 (0) | 2022.05.13 |
증감 연산자(Increment & Decrement Operators) (0) | 2022.05.11 |
new Date() 란? (0) | 2022.05.11 |
if 조건문 사용해서 홀수,짝수 구해보기 (0) | 2022.05.09 |