mouseover/mouseout: 세부 동작과 relatedTarget
mouseover
마우스 커서가 어떤 요소 위에 들어왔을 때 발생합니다.
- event.target: 현재 마우스가 위치한 요소
- event.relatedTarget: 마우스가 이전에 있던 요소(창 밖에서 진입 시 null)
mouseout
마우스 커서가 요소를 벗어날 때 발생합니다.
- event.target: 마우스가 벗어난 요소
- event.relatedTarget: 포인터가 이동한 새로운 요소 (창 밖일 경우 null)
자식 요소 간 이동도 이벤트로 인식되며, 버블링이 발생해 상위 요소에서도 감지 가능합니다.
예제: 마우스 이동 경로 추적하기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>마우스 이동 경로 추적</title>
<style>
#tracker { width: 400px; height: 200px; border: 2px solid #444; position: relative; }
#inner { width: 100px; height: 100px; background: #e0f7fa; position: absolute; top: 50px; left: 150px; }
#log { margin-top: 10px; width: 400px; height: 100px; }
</style>
</head>
<body>
<div id="tracker">컨테이너
<div id="inner">내부 요소</div>
</div>
<textarea id="log" readonly></textarea>
<script>
const tracker = document.getElementById('tracker');
const log = document.getElementById('log');
function logEvent(e) {
const target = e.target.id || e.target.tagName;
const related = e.relatedTarget ? (e.relatedTarget.id || e.relatedTarget.tagName) : '외부';
log.value = `${e.type}: ${target} -> ${related}\n${log.value}`;
}
tracker.addEventListener('mouseover', logEvent);
tracker.addEventListener('mouseout', logEvent);
</script>
</body>
</html>
mouseenter/mouseleave: 단순화된 요소 진입 / 탈출
- mouseenter와 mouseleave는 mouseover/mouseout과 유사하게 동작하지만, 자식 요소 간의 이동은 무시됩니다.
- 버블링이 발생하지 않으므로 이벤트 위임(event delegation)을 사용할 수 없다는 단점이 있지만, 내부 이동에 따른 불필요한 이벤트 발생을 막아줍니다.
예제: 단순 요소 이동 사용
<!-- index.html -->
<div id="hoverBox" style="width:300px; height:200px; background-color: #f0f0f0; margin-bottom:10px; position: relative;">
마우스를 올려보세요.
<div style="width:150px; height:100px; background-color: #cce5ff; position: absolute; top:50px; left:75px;">
내부 박스
</div>
</div>
<p id="status"></p>
<script>
const hoverBox = document.getElementById('hoverBox');
const status = document.getElementById('status');
hoverBox.addEventListener('mouseenter', () => {
status.textContent = 'mouseenter: 박스에 진입';
});
hoverBox.addEventListener('mouseleave', () => {
status.textContent = 'mouseleave: 박스에서 탈출';
});
</script>
버블링이 발생하지 않으므로, 부모 요소에서 자식 요소로 이동해도 이벤트가 발생하지 않습니다.
빠른 마우스 이동과 누락 처리
브라우저는 성능을 위해 마우스의 위치를 일정 간격으로 체크합니다. 이 때문에 마우스가 매우 빠르게 이동하면 중간에 있는 요소들을 건너뛰게 될 수 있습니다. 예를 들어, 두 요소 사이의 mouseover/mouseout 이벤트는 건너뛰어 바로 첫 요소에서 마지막 요소로 이벤트가 발생할 수 있습니다.
따라서 이벤트 처리 시 관련된 요소(relatedTarget)가 null일 수 있음을 염두에 두고 예외 처리를 하는 것이 좋습니다.
mouseover/mouseout: 이벤트 위임 활용
mouseenter/mouseleave는 버블링하지 않기 때문에, 많은 자식 요소에 대한 처리가 필요한 경우 이벤트 위임을 적용하기 어렵습니다.
대신, mouseover와 mouseout을 활용해 이벤트 위임을 구현할 수 있습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>메뉴 항목 강조</title>
<style>
#menu { padding: 0; list-style: none; width: 200px; border: 1px solid #ddd; }
li { padding: 10px; cursor: pointer; }
.highlight { background: #fff176; }
</style>
</head>
<body>
<ul id="menu">
<li>항목 1</li>
<li>항목 2 <span>(부가 정보)</span></li>
<li>항목 3</li>
</ul>
<script>
const menu = document.getElementById('menu');
let current = null;
menu.addEventListener('mouseover', (e) => {
const li = e.target.closest('li');
if (!li || li === current) return;
if (current) current.classList.remove('highlight');
li.classList.add('highlight');
current = li;
});
menu.addEventListener('mouseout', (e) => {
if (!current) return;
const related = e.relatedTarget;
if (related && current.contains(related)) return;
current.classList.remove('highlight');
current = null;
});
</script>
</body>
</html>
'브라우저' 카테고리의 다른 글
포인터 이벤트: 통합 입력 처리 (0) | 2025.02.20 |
---|---|
마우스 이벤트로 구현하는 드래그 앤 드롭 (0) | 2025.02.20 |
마우스 이벤트 완벽 분석 (0) | 2025.02.20 |
커스텀 이벤트와 이벤트 디스패치 (0) | 2025.02.20 |
이벤트 위임과 브라우저 기본 동작 제어 (0) | 2025.02.20 |