이벤트 플로우, 이벤트 흐름이란 무엇일까.
평범하게 이런 html 코드가 있다고 가정해보자.
<html> <body> <div>Click Here!</div> </body> </html>
상술한 코드에서 div
요소가 클릭 되었을때(onclick)를 감지해서 함수가 실행되도록 코드를 짜고 div
요소를 클릭했을때, 만약 div
의 상위 요소인 body
와 html
등에도 클릭 이벤트 핸들러가 짜여 있다면 body
와 html
의 클릭을 감지하는 이벤트 핸들러는 어떻게 될까?
정답은 '사이좋게 실행된다.' 이다.
여기서 이벤트를 직접 실행시킨 div
가 이벤트의 원인이되는 것이고, body
와 html
은 이벤트에 탑승했다고 한다.
그런데 우애 좋은 형제 사이에도 찬물 마실땐 위 아래가 있듯, 사이좋게 실행되더라도 먼저 실행되는 윗 놈(이벤트)이 있을 것이다.
이 이벤트들이 실행되는 순서를 사람들은 Event Flow
라고 부른다.
이벤트 플로우를 알아보기 전에 target
과 currentTarget
이 어떻게 다른지부터 짚고 가자.
우선 개괄적으로 한번 짚고 지나가자면 target
은 시작점을 뜻하고 currentTarget
은 지금 실행 중인 이벤트가 어디서 실행중인지를 의미한다.
이렇게만 말하면 느낌이 바로 안오니 위에서 만들어 놓은 HTML을 보면서 이야기 해보자
<html> <body> <div>Click Here!</div> </body> </html>
여기서 우리는 모든 요소에 클릭 이벤트 핸들러를 짜 놓았다.
여기서 div
를 클릭해 이벤트가 발생했을때,
div 이벤트 핸들러
에서는 target
은 div
가 되고 currentTarget
역시 div
가 된다.
그럼 body 이벤트 핸들러
에서는 어떻게 될까?
target
은 이벤트의 시작점인 div
가 되고 currentTarget
은 누가 어디서 이벤트를 실행했던, 실행된건 body
에 연결된 이벤트 핸들러이니 body
가 된다.
html
의 경우도 target
은 div
, targetCurrent
는 html
이 된다.
우선 어디에서든지 이벤트가 발생하면 root
에서 가까운 순으로 이벤트가 실행된다.
<html> <body> <div>Click Here!</div> </body> </html>
우리가 예제로 삼고있는 코드에서는 html
이 가장 루트에 가까우니 html을 요소로 가지는 이벤트 핸들러가 가장 먼저 실행이 되게 되는 것이다.
그 다음엔 body
에 연결된 이벤트 핸들러 그리고 나서 드디어 div
에 연결된 이벤트 핸들러가 실행되게 된다.
여기서 div
에 연결되기 전까지의 단계를 Capture Phase 라고 하고 div
에 연결된 이벤트 핸들러가 실행되는 단계를 Target Phase라고 한다.
target
인 div
에 연결된 이벤트 핸들러가 실행되고 나면 다시 역순으로 root로 돌아가며 이벤트 핸들러들을 실행시킨다.
이 경우 body
와 html
에 연결된 이벤트 핸들러들이 body
, html
순서로 다시 한번 실행되는 것이다.
이 단계를 Bubble Phase 라고 하며 이 Bubble Phase를 끝으로 이벤트는 종료된다.
즉 이벤트 플로우는 Capture Phase -> Target Phase -> Bubble Phase 순으로 이어진다.
이걸 그림으로 나타내면 다음과 같다.
필체가 상당히 더럽다
이 페이즈 대로 실행된다면 div 의 상위 요소에 연결된 이벤트 핸들러들은 두번씩 실행이 되고만다.
하지만 현명한 브라우저님은 이 상위 요소에 연결된 이벤트 핸들러들, 즉 target
과 currentTarget
이 일치하지 않는 이벤트 핸들러들에 대해 한번씩 실행되게 제약을 걸고 Capture Phase에서 실행될 것인지 Bubble Phase에서 실행될 것 인지 선택하게 한다.
선택하는 방법은 닷 노테이션으로 이벤트 핸들러를 표현할 땐 사용할 수 없고 addEventListener
메소드를 사용해야한다.
addEventListener
의 문법은 target.addEventListener(type, listener[, useCapture]
인데,
여기서 useCapture
자리에 불리언 타입을 인자로 받는데 여기에 true
를 넣으면 Capture Phase에서 이벤트 핸들러가 실행되고 기본 값인 false
를 넣으면 Bubble Phase에서 실행되게 된다.
즉, 따로 불리언 타입을 넣지 않으면 Bubble Phase에서 실행되기에 target
의 이벤트 핸들러가 가장 먼저 실행되게 된다.
<html> <body> <div>Click Here!</div> </body> </html>
let html = document.documentElement; let body = document.body; let div = document.querySelector('div'); html.addEventListener('click', function(){ console.log('I\'m Html Event Handler!') }) body.addEventListener('click', function(){ console.log('I\'m Body Event Handler!') }) div.addEventListener('click', function(){ console.log('I\'m Div Event Handler!') })
기본 값이 false
라 Bubble Phase에 실행된 것을 볼 수 있다.
let html = document.documentElement; let body = document.body; let div = document.querySelector('div'); html.addEventListener('click', function(){ console.log('I\'m Html Event Handler!') }) body.addEventListener('click', function(){ console.log('I\'m Body Event Handler!') }, true) div.addEventListener('click', function(){ console.log('I\'m Div Event Handler!') })
여기선 body
에 연결된 이벤트 핸들러가 Capture Phase에 실행되었기 때문에 가장 먼저 실행된 것을 볼 수 있다.
기본 값이 false로 받아 버블 페이즈로 실행되서 아마 크게 쓸일이 없을 것 같긴하지만, 미래에 이걸 정리 안해서 고생할 나를 위해 미리 정리한다.
오랜만에 TIL 빼고 OiMW에서 열심히 포스팅한 것 같은데 TIL에 적어놓은거 OiMW로 다시 정리할거 생각하면 머리가 아파온다.
김버그님 유튜브
정말 잘 봤습니다 이해 너무 잘 됐어요 🙏