상단바를 제작하고 특정 항목에 마우스를 가져다대면 상단바가 확장되면서 해당 메뉴에 맞는 하위항목을 보여주도록 만들고있었다. 그리고 다시 확장된 상단바 영역에서 마우스가 빠져나가면 하위메뉴가 사라지고 상단바는 원래의 크기로 돌아가도록 구현하고있었다.
그런데 분명 상단바에 마우스이벤트를 등록했음에도 상단바 영역이 아닌 특정 항목을 빠져나가기만 하면(항목 => 상단바 영역으로 마우스 이동) 이벤트가 감지되어 상단바 크기가 원래대로 돌아갔다. 이는 이벤트 버블링 현상 때문이다.
마우스커서를 가져다댈때의 동작은 onMouseOver, onMouseEnter를 사용할 수 있고,
마우스커서가 빠져나올때의 동작은 onMouseOut, onMouseLeave를 사용할 수 있다.
동일한 동작임에도 각각 두개의 이벤트핸들러가 존재하는 이유는 이벤트버블링의 유무를 정할수 있도록 하기 위해서이다.
드롭다운 메뉴 예시를 통해 마우스이벤트와 이벤트버블링을 이해할 수 있다.
1. 마우스커서가 요소 위로 올라갈때
마우스커서를 특정 요소에 가져다댈때는 onMouseOver와 onMouseEnter를 사용할 수 있으며 단순히 기능을 구현해보면 둘 사이의 기능 차이는 없다.
하지만 onMouseOver는 이벤트버블링이 발생하며 onMouseEnter는 이벤트버블링이 발생하지 않는다.
이벤트 버블링에 대해서는 2번 항목에서 자세하게 다룬다.
2. 마우스커서가 요소에서 빠져나올때
마우스커서를 올렸을때 하위항목이 표시되는 드롭다운 메뉴를 만들때 이벤트핸들러 사이의 차이를 알 수 있다.
2-1. 마우스 이벤트로 드롭다운 메뉴 구현
드롭다운 메뉴는 특정항목 버튼을 누르거나 마우스를 가져다댔을때 그 하위메뉴가 표시되는 메뉴를 뜻한다.
메뉴에 마우스를 가져다대면 하위메뉴가 표시되고, 메뉴의 전체영역(부모요소)인 회색영역을 마우스가 벗어나면 다시 메뉴가 닫히도록 설계하려한다.
드롭다운 메뉴 전체를 감싸는 회색영역이 부모요소, 메뉴버튼이 자식요소일때 회색영역인 부모요소에 onMouseOut을 사용하여 메뉴를 닫히도록 하면 아래와 같이 동작한다.
회색영역인 부모요소에 onMouseOut을 사용했음에도 메뉴버튼에서 마우스가 벗어나면 메뉴가 닫혀버린다. 이런식으로 동작하게되면 하위요소를 제대로 클릭할 수 없게된다. 여기서 부모요소에 onMouseLeave를 사용하면 아래와 같이 동작한다.
onMouseLeave를 사용하면 의도한대로 작동하는 것을 알 수 있는데 이는 이벤트버블링 때문이다.
2-2. 이벤트버블링
이벤트버블링이란 특정 요소에서 이벤트가 발생했을때 그 부모요소까지 전파되는것을 의미한다.
위 드롭다운 예시로 설명하자면 자식요소(메뉴 버튼)에서 "마우스가 벗어났다" 라는 이벤트가 발생했을때 부모요소(회색 영역)에서도 마우스가 벗어났다는걸 인식하고 이벤트핸들러가 작동하게 되는것이다.
이는 클릭, 더블클릭이나 마우스움직임 등 마우스이벤트 전반에서 일어나는 현상이며 stopPropagation을 사용하면 강제로 이벤트 버블링을 막을수 도 있다. 아래 예시에서 이벤트 버블링과 stopPropagation의 역할을 확인할 수 있다.
※ 자식요소를 클릭해도 이벤트버블링이 발생해 부모요소가 같이 클릭된다.
See the Pen click event without stopPropagation by PARKSEONGSU (@ahuiogadjfgo) on CodePen.
※ stopPropagation을 사용하여 자식요소를 클릭할때는 부모요소가 함께 클릭되지 않는다.
See the Pen click event with stopPropagation by PARKSEONGSU (@ahuiogadjfgo) on CodePen.
이벤트버블링 현상은 JSX에서도 동일하게 발생하며 stopPropagation도 사용이 가능하다.
마우스이벤트에서 발생하는 이벤트버블링 현상은 개발전반에서 유용하게 사용되며 꼭 막아야할 상황에는 stopPropagation을 사용하면 된다.
이벤트핸들러의 경우 버블링이 일어나지않는 핸들러가 따로 제공되는 경우도 있으므로 필요에따라 사용할 수 있다.
'Web > React' 카테고리의 다른 글
React의 key (Diffing 알고리즘과 framer-motion) (1) | 2024.10.23 |
---|---|
Styled-Components의 transient props ($ 기호로 prop 사용하기) (0) | 2024.06.27 |
JSX에서 조건문 사용하기 (0) | 2024.04.17 |
무한스크롤 : IntersectionObserver와 useRef (0) | 2024.04.14 |
useState의 setter함수와 함수형 업데이트 (0) | 2024.04.14 |