[TDI] mouseout과 this의 조합하여 이용해서 신경 써야 하는 부분

2025. 11. 23. 18:25웹 개념

728x90

소개

웹 개발에서 드롭다운 메뉴를 구현할 때, 마우스 오버(Hover) 상태가 서브 메뉴로 이어지지 못하고 메뉴가 즉시 사라지는 현상은 흔한 오류입니다. 이 문제의 근본 원인은 mouseout 이벤트가 작동하는 방식과 이벤트 핸들러 내부에서 요소를 잘못 참조했기 때문에 발생합니다. 이 글에서는 문제의 원인을 명확히 분석하고, mouseover/mouseout을 사용하면서도 메뉴를 안정적으로 구현하는 방법을 제시합니다.


1. 문제의 진짜 원인: mouseout과 event.target 재탐색

문제는 mouseover/mouseout 이벤트 자체에 있다기보다는, 이벤트 리스너가 **<li>**에 붙어 있음에도 핸들러 내부에서 **event.target.closest("li")**를 사용하여 요소를 다시 탐색했기 때문에 발생했습니다.

오작동 코드의 로직과 충돌

JavaScript
 
// 리스너는 개별 LI 요소에 붙어 있음 (this는 LI)
menu.addEventListener("mouseout", function (event) {
  // 💥 문제의 코드: 마우스가 서브 메뉴(자식 요소)로 이동하면 'mouseout'이 발생함
  const menuItem = event.target.closest("li"); // target은 자식 요소, closest는 다시 부모 LI를 찾음
  const subMenu = menuItem.querySelector(".sub-menu");
  subMenu.style.display = "none"; // LI의 영역을 벗어났다고 오해하여 서브 메뉴를 닫아버림
});

오류 메커니즘

  1. mouseout의 특성: mouseout 이벤트는 마우스 커서가 부모 요소 (<li>)에서 자식 요소 (<ul>)로 이동할 때도 발생합니다.
  2. 재탐색의 오류: mouseout이 발생했을 때, 핸들러가 실행됩니다. 핸들러는 event.target.closest("li")를 통해 **현재 <li>**를 다시 찾고, 이 요소를 기준으로 서브 메뉴를 닫는 명령어를 실행합니다.
  3. 결과: 마우스가 서브 메뉴 위에 있음에도 불구하고, mouseout이 발생했기 때문에 닫기 명령이 실행되어 메뉴가 사라집니다.

2. ✅ 핵심 해결책: 리스너 대상인 this 활용

이 문제를 해결하는 가장 간단하고 정확한 방법은 이벤트 리스너가 실제로 붙어 있는 요소를 참조하는 this 키워드를 사용하는 것입니다.

this는 리스너가 붙은 <li> 요소와 그 **모든 자식 요소 (<ul>)**를 하나의 영역으로 간주합니다. 따라서 마우스가 <li> 영역 내부(서브 메뉴 포함)에 있는 한, mouseout 이벤트는 발생하지 않습니다.

🛠️ 정상 작동하는 JavaScript 코드

JavaScript
 
function solution() {
  // 이벤트 리스너를 개별 LI 요소에 부착
  const mainMenu = document.querySelectorAll(".main-menu > li");

  mainMenu.forEach(function (menu) {
    // 마우스가 LI 영역에 들어올 때
    menu.addEventListener("mouseover", function (event) {
      // this는 현재 LI 요소를 가리킴
      const subMenu = this.querySelector(".sub-menu"); 
      subMenu.style.display = "block";
    });

    // 마우스가 LI 영역(서브 메뉴 포함)에서 완전히 벗어날 때만 발생
    menu.addEventListener("mouseout", function (event) {
      // this는 현재 LI 요소를 가리킴 (event.target.closest("li") 재탐색 불필요)
      const subMenu = this.querySelector(".sub-menu"); 
      subMenu.style.display = "none";
    });
  });
}
document.addEventListener("DOMContentLoaded", solution);

3. ✨ event.target과 this의 역할 비교

구분 this event.target
가리키는 요소 이벤트 리스너가 부착된 요소 (<li>). 이벤트가 실제로 처음 발생한 가장 깊은 요소 (<a> 태그 등).
이번 예시의 역할 <li>와 그 자식 요소 전체를 하나의 '호버 영역'으로 정의하며, 자식 요소를 참조할 때 가장 안전합니다. 마우스가 어디서 버블링을 시작했는지 확인하는 용도로 사용됩니다.

 

결론적으로, 드롭다운 메뉴와 같이 부모-자식 관계가 밀접한 구조에서는 event.target을 이용한 재탐색 대신, 리스너가 붙은 요소를 명확히 가리키는 **this**를 사용하여 코드를 간결하고 안정적으로 만드는 것이 핵심입니다.

728x90