이벤트의 여행

페이지에서 이벤트가 발생하면 DOM 전체 계층에 있는 모든 요소가 이벤트를 처리할 기회를 가진다. 페이지가 다음 화면과 같다고 가정하자.

							<div class="foo" id="foo">
								<span class="bar">
									<a href="http://www.naver.com" id="link">재빠른 갈색 여우가 게으른 강아지를 뛰어넘는다.</a>
								</span>
								<p>
									댓글은 통신 예절 지키면서 표현 자유 추구하는 방향으로.
								</p>
							</div>
						

다음 그림은 코드에서 중첩된 요소들의 집합을 시각적으로 나타내고 있다.

재빠른 갈색 여우가 게으른 강아지를 뛰어넘는다.

댓글은 통신 예절 지키면서 표현 자유 추구하는 방향으로.


어떠한 이벤트에 대해서든 논리적으로 여러 요소가 반응에 대한 책임이 있을 수 있다. 이 페이지에서 앵커(a)가 클릭되면 div, span, a 모두 클릭 이벤트에 응답할 기회가 생긴다. 그 이유는 세 개가 모두 사용자의 마우스 커서 아래에 있기 때문이다. 반면 p요소는 전혀 이러한 상호작용의 대상이 되지 않는다.

여러 요소가 사용자의 상호작용에 모두 반응하게 하는 전략을 이벤트 캡처링(event capturing)이라고 한다. 이벤트 캡처링에서는 모든 것을 감싸고 있는 최상위의 요소에게 이벤트가 처음으로 주어지고 그 후에 연속해서 하위 요소로 전달된다. 이 예제에서는 가장 먼저 div가 이벤트를 받게 되고, span이 그 뒤를 이어서 받고, 마지막으로 a가 받게 된다. 다음 그림을 참고하라.

반대 방향으로 이벤트가 전파되는 것을 이벤트 버블링(event bubbling)이라고 한다. 이벤트는 가장 낮은 자식 요소에 처음으로 전달되고, 해당 요소가 반응한 후에, 그 이벤트는 그 부모 요소로 전파된다. 다음 그림에서 보는 것처럼 이 예제에서는 a 요소가 이벤트를 첫 번째로 처리하고, span 과 div 가 차례대로 처리하게 된다.

당연한 이야기지만 브라우저 개발자는 브라우저를 개발하면서 서로 다른 이벤트전파 모델을 구현했다. 그런데 나중에 만들어진 DOM 표준은 결국 이 두가지 전략을 모두 사용해야 한다고 규정하고 있다. 이벤트는 먼저 부모에서 특정 자식 요소로 캡처링되고 나서 다시 최상위 DOM 계층으로 버블링되어 올라간다. 이벤트 핸들러는 두 가지 과정 각각에 등록할 수 있다.

아직 모든 브라우저가 이 새로운 표준을 따르도록 업데이트되지는 않은 상태이며 보통 캡처링을 사용하려면 반드시 명시적으로 지정해야 한다. jQuery는 브라우저 호환의 일관성을 제공하려고 항상 두가지 가운데 버블링 단계에 이벤트 핸들러를 등록한다. 그 결과 이벤트에 첫 번재로 응답하는 것이 항상 선택된 최하위 특정 자식 요소임을 보장받을 수 있다.



이벤트 버블링의 부작용

이벤트 버블링은 기대하지 않은 결과를 낳을 수 있는데, 특히 mouseover 나 mouseout에서 엉뚱한 요소가 응답할 때가 더욱 그렇다. 예제에서 mouseout 이벤트 핸들러가 div 태그에 연결됐다고 가정하자. 사용자의 마우스 커서가 div를 벗어난다면 mouseout 핸들러가 예상한 대로 잘 진핼될 것이다. 왜냐하면 div 계층 구조상 가장 높은 위치에 있으므로 다른 요소에게 이 이벤트가 전달되지 않기 때문이다. 이와 반대로 커서거 a 요소를 벗어난다면 mouseout 이벤트는 a 태그로 보내진다. 이 이벤트는 위로 버블링돼 상위에 있는 span 태그로도 전달될 것이고, 그 후에 div 태그로도 전달돼 div의 mouseout 이벤트 핸들러가 실행될 것이다. 이런 버블링은 원하는 바가 아니다.

.mouseenter와 .mouseleave 이벤트는 개별적으로 연결되거나 .hover() 메서드로 동시에 사용되거나에 관계없이 이러한 버블링 문제를 고려하고 있으며 핸들러를 추가할 때 이 이벤트를 사용하면 mouseover나 mouseout 이벤트가 엉뚱한 요소에 전달돼서 발생하는 문제를 방지할 수 있다.

mouseover을 사용한 시나리오는 이벤트의 전파 범위를 제한해야 할 필요성을 보여준다. 위와 같은 특수한 상황은 .hover()로 해결할 수 있지만, 이런 상황 외에도 이벤트를 공간적으로 제한(특정 요소로 전달되는 것을 막는 것)하거나 시간상으로 제한(특정 시점에 이벤트의 발생을 막는 것)해야 할 수도 있다.