Javascript - 이벤트 버블링/캡처링
첫 기업 과제테스트를 하면서 중요성을 느낀 바닐라 JS의 주요개념 이벤트 버블링/캡처링에 대해서 정리하고자 한다 ㅎㅎ 오랜만에 작성하는 블로그 인 만큼 재미있게 정리해보자. 물론 과제 테스트에서 탈락의 고배를 마셨지만 이벤트 버블링과 캡처링은 프론트엔드 엔지니어로서 꼭 알아야되는 Js 개념이다
<참고 레퍼런스>
https://ko.javascript.info/bubbling-and-capturing
<이벤트 버블링>
개념 : 한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작하는 것을 의미한다.
개념에 의거하자면 가장 안쪽 요소인 <p> 태그를 시작으로 document 객체를 만날때 까지 각 요소에 할당된 onClick 핸들러가 동작한다. 이벤트가 제일 깊은 곳에 있는 요소에서 시작해 부모 요소를 거슬러 올라가며 발생하는 모양이 마치 물속 거품과 비슷하다고 하여 이벤트 버블링 이라고 한다.
지금 이렇게 정리하면서 느낀 건 이벤트 버블링 개념을 간과했기 때문에 과제테스트에 영향이 있었던 것 같다. 공식문서에서 말하기를 이벤트 버블링에 대해서 이렇게 서술한다.
<이벤트 버블링 막기>
꼭 필요한 경우를 제외하곤 버블링을 막지 마세요!
버블링은 유용합니다. 버블링을 꼭 멈춰야 하는 명백한 상황이 아니라면 버블링을 막지 마세요. 아키텍처를 잘 고려해 진짜 막아야 하는 상황에서만 버블링을 막으세요.event.stopPropagation()은 추후에 문제가 될 수 있는 상황을 만들어낼 수 있습니다.문제가 발생할만한 시나리오를 살펴봅시다.중첩 메뉴를 만들었다 가정합시다.
각 서브메뉴(submenu)에 해당하는 요소에서 클릭 이벤트를 처리하도록 하고, 상위 메뉴의 클릭 이벤트 핸들러는 동작하지 않도록 stopPropagation을 적용합니다.사람들이 페이지에서 어디를 클릭했는지 등의 행동 패턴을 분석하기 위해, window내에서 발생하는 클릭 이벤트 전부를 감지하기로 결정합니다. 일부 분석 시스템은 그렇게 분석합니다.
이런 분석 시스템의 코드는 클릭 이벤트를 감지하기 위해 document.addEventListener('click'…)을 사용합니다. stopPropagation로 버블링을 막아놓은 영역에선 분석 시스템의 코드가 동작하지 않기 때문에, 분석이 제대로 되지 않습니다. 안타깝게도 stopPropagation을 사용한 영역은 '죽은 영역(dead zone)'이 되어버립니다.
버블링은 왠만하면 막지 않는 것이 정석이라는 글을 보면서 내가 과제테스트에서 구현했던 로직이 틀렸구나를 알 수 있었다. 간략하게 내가 구현했던 부분에 말하자면 고양이의 정보를 수정하는 Modal 컴포넌트가 있고 해당 컴포넌트에서 고양이 정보의 제목을 수정하는 기능이었다.
modalTitleInput.onkeydown = (event: KeyboardEvent) => {
event.stopPropagation()
if (event.key === 'Enter') {
const newTitle = modalTitleInput.value
// 로컬스토리지 업데이트
const storedData = localStorage.getItem(data.id)
if (storedData) {
const parsedData = JSON.parse(storedData)
parsedData.title = newTitle
localStorage.setItem(data.id, JSON.stringify(parsedData))
console.log(`Updated title in localStorage: ${parsedData.title}`)
}
modalTitleInput.blur() // 엔터 후 포커스 해제
}
}
해당 타이틀을 수정할 때 modalTitleInput 컴포넌트의 상위 요소로 이벤트가 전파되지 않도록 하기 위해 event.storPropagation() 함수를 사용한 것을 알 수 있다..!
<event.target Vs event.currentTarget (this)>
이벤트가 발생한 가장 안쪽의 요소는 타깃(target) 요소라고 불리고, event.target을 사용해 접근할 수 있다. 이처럼 event.target은 실제 이벤트가 시작된 타겟 요소를 의미한다.
반대로 event.currentTarget(this)는 현재 실행 중인 핸들러가 할당된 요소를 의미한다. 이 부분이 바로 이해가 가지 않았지만 공식문서의 코드와 설명을 보면서 바로 이해할 수 있었다.
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);
};
form을 클릭했을 때는 event.target과 event.currentTarget이 같을 것이다. 하지만 form 태그 내부에 있는 div 태그를 클릭하면 결과는 달라진다. 즉, onClick이라는 핸들러가 form 요소에 있던 핸들러 이기 때문에 event.currentTarger은 form 요소를 가리키고 있는 것이다. 그래서 event.target은 div를 의미하고 event.currentTarget은 form을 의미하게 된다.
<이벤트 캡처링>
캡처링 개념은 개발 시에 자주 쓰이지는 않지만 알아두면 좋다. 일단 정리 겸 표준 DOM 이벤트에서 정의한 3 가지 이벤트 흐름이 있는데 다음과 같다.
캡처링 단계에서 이벤트를 잡아낼려면 addEventListener의 capture 옵션을 true로 설정해야 한다. 기본 capture 옵션은 false 이고 false 일때는 버블링 단계에서 동작한다.
그리고 만약 capture 옵션이 true인 addEventListener와 기본 속성의 addEventListener가 호출된다면 타깃 단계는 2번 발생한다. (버블링과 캡처링 과정의 중간에 껴있는 친구이기 때문)
junyound.addEventListener('onClick', true)
<회고>
요새 계속 서류를 넣어보고 코딩테스트를 봐보면서 경험을 쌓고 있다. 서류를 붙어도 코딩테스트에서 떨어지고 서류에서도 계속 떨어지다보니 자존감도 떨어지고 하지만 취준 시장의 본질은 1승만 한다면 승리자가 되는 것이다..ㅎㅎ 기분이 안 좋을 때 마다 1승만 챙기면 승리자네? 오히려 유리해 라는 긍정적인 마인드로 버티고 있는거 같다.
과제 테스트 이후에 블로그를 쓰자라고 해놓고 10월 마지막 날이 되서야 쓰게 된 오늘의 블로그 였다. 다시 달려보는 것을 목표로 해보자! 다음 주제는 자바스크립트의 이벤트 위임이다.