Javascript Cookbook

중첩된 요소로 이벤트 전파 방지하기

문제

중첩된 요소가 있고 두 요소 모두 클릭 이벤트를 캡처한다. 안쪽 요소에서 발생한 클릭 이벤트가 바깥쪽 요소로 전파되는 것을 막고 싶다.

해결

전파되는 이벤트를 일반적인 방식으로 중단한다.

//이벤트 전파 중단
function cancelPropagation(event){
    if(event.stopPropagation){
        event.stopPropagation();
    }else{
        event.cancelBubble = true;
    }
}

안쪽 요소를 클릭할 때 실행되는 이벤트 핸들러에서 이 함수를 호출하고 이벤트 객체를 인수로 전달하면 된다.

cancelPropagation(event);

설명

이벤트를 취소하고 싶지는 않지만 전파되는 것을 막고 싶다면 이벤트가 발생했을 때 이벤트 전파를 중단시켜야 한다.

크롬, 사파리, 파이어폭스는 stopPropagation 메서드를 지원하지만 IE8에서는 cancelBubble 속성을 true로 설정해야 작동한다.

<!DOCTYPE html>
<html>
<head>
<title>이벤트 전파 방지</title>
<meta charset="utf-8">
<style>
#one
{
   width: 100px; height: 100px; background-color: #0f0;
}
#two {
   width: 50px; height: 50px; background-color: #f00;
}
#stop
{
  display: block;
}
</style>
<script>

// 전파 취소 여부를 가리키는 전역변수
var stopPropagation = false;

// 재사용 가능한 이벤트 처리함수
function listenEvent(eventTarget, eventType, eventHandler) {
   if (eventTarget.addEventListener) {
      eventTarget.addEventListener(eventType, eventHandler,false);
   } else if (eventTarget.attachEvent) {
      eventType = "on" + eventType;
      eventTarget.attachEvent(eventType, eventHandler);
   } else {
      eventTarget["on" + eventType] = eventHandler;
   }
}

// 이벤트 전파 취소
function  cancelPropagation (event) {
   if (event.stopPropagation) {
      event.stopPropagation();
   } else {
      event.cancelBubble = true;
   }
}

// 이벤트 등록
listenEvent(window,"load",function() {
   listenEvent(document.getElementById("one"),"click",clickBoxOne);
   listenEvent(document.getElementById("two"),"click",clickBoxTwo);
   listenEvent(document.getElementById("stop"),"click",stopProp);
  });

function stopProp() {
    stopPropagation = true;
}

function clickBoxOne(evt) {
  alert("Hello from One");
}

function clickBoxTwo(evt) {
  alert("Hi from Two");
  if (stopPropagation) {
     cancelPropagation(evt);
  }
}
</script>

</head>
<body>
    <div id="one">
        <div id="two">
            <p>Inner</p>
        </div>
    </div>
    <button id="stop">Stop Propagation</button>
</body>
</html>

예제보기 http://embed.plnkr.co/Ed9HXXxlSEDIB7UkMIkV/preview

다음은 좀더 효율적인 클릭 이벤트 처리함수이며 예제에 있던 두 개의 함수를 하나로 합친 것이다.

function clickBox(evt) {
  evt = evt || window.event;
  alert("Hi from " + this.id);
  if (stopPropagation) {
     cancelPropagation(evt);
  }
}

stopPropagation이 false로 설정되면 두 요소 모두 이벤트를 받는다. 이벤트 처리함수는 접근한 요소의 아이디를 this를 통해 알아내어 메시지를 작성한다.

하지만 IE8은 이 함수를 지원하지 않는다. attachEvent는 DOM이 아니라 window 객체를 통해 이벤트를 처리하므로 요소 콘텍스트 this를 사용할 수 없다. 이벤트를 받은 객체는 이벤트 객체의 srcElement 속성을 통해 접근할 수 있지만 이벤트를 처음 받은 요소로 설정될 뿐 전파된 요소로 업데이트 되지 않는다.

관련내용 IE7,8 attachEvent this처리

대신 DOM 레벨 0 이벤트 핸들링에서는 이러한 문제가 발생하지 않는다. 이때는 IE도 전파와는 무관하게 이벤트 처리 함수 안에서 요소 콘텍스트 this에 접근할 수 있다. 두 개의 div에 이벤트 핸들러를 직접 할당한다.

document.getElementById("one").onclick = clickBox;
document.getElementById("two").onclick = clickBox;