본문 바로가기

Javascript/DOM과 HTML 제어

stopPropagation() 과 preventDefault() 의 차이

반응형

이벤트 리스너로 이벤트를 처리할 때 이벤트 흐름을 바꾸거나 취소할 수 있는 메서드로 stopPropagation() 과 preventDefault() 가 있습니다.

둘은 이벤트를 취소하는 기능으로는 유사하지만, 실제 사용하는 방법이나 적용 결과가 전혀 다릅니다.



preventDefault()


preventDefault() 는 타겟의 이벤트를 취소하고, 기본 동작 또한 취소합니다. 콜백 함수 부분만 실행됩니다.

폼 서밋 버튼을 클릭했다거나, 마우스 오른쪽 클릭으로 컨텍스트 메뉴를 연다거나, 링크 클릭으로 다른 페이지로 이동한다거나 하는 동작을 취소합니다.


preventDefault() 와 관련해서 가장 빈번한 이슈가 <a href="#">저장</a> 문제입니다.

앵커 링크로 특정 기능을 하는 버튼을 구현한 경우 href 에 "#" 을 넣는 경우가 많습니다.


앵커 링크 URL 대신 "#" 을 사용하면 링크를 클릭해도 현재 페이지에 머무르기 때문에 자주 사용합니다. 

"#"은 정확하게 하면 현재 페이지의 최 상단으로 이동합니다.


앵커 링크에 이벤트 리스너를 붙인 경우, 페이지 스크롤이 없거나, 페디지 최상단이면 이벤트 리스터만 실행되고 페이지에 아무런 변화가 없으므로 꽤 좋은 방법이 됩니다.

그런데 페이지 스크롤 된 상태면, 클릭할 때마다 이벤트 스크롤과 함께 페이지 최상단으로 올라가는 현상이 생깁니다.

이것은 이벤트 리스너 콜백과는 별도로 앵커 링크를 클릭하면 href 의 링크로 이동하는 기본 이벤트가 실행되기 때문입니다.


이런 문제를 방지하려면 preventDefault() 를 링크 앵커에 등록한 이벤트 리스너 콜백 함수에 사용해 기본 이벤트 동작이 실행되지 않도록 막아야 합니다.


아래와 같이 콜백 함수를 작성하면 페이지 최상단으로 이동하는 문제없이 깨끗한 이벤트 리스너 처리를 할 수 있습니다.


<a href="#" id="showlist">보기</a>

document.getElementById('showlist').addEventListener('click',function(e){
    console.log('목록 출력!');
    e.preventDefault();
  });




stopPropagation()


stopPropagation() 은 타겟의 콜백 함수 및 기본 동작을 실행하지만, 상위, 또는 하위 요소로의 이벤트 전파는 차단합니다.


이벤트 버블링으로 이벤트를 전파하는 경우
이벤트 타겟 엘리먼트에 등록된 이벤트와 같은 타입의 이벤트를 상위 요소에도 등록한 경우, 이벤트가 버블링 되면서 상위 요소의 콜백 함수도 실행됩니다.

상위 요소의 이벤트가 실행되는게 의도한게 아닌 경우, 이벤트 타겟의 콜백 함수에 stopPropagation() 을 사용해 이벤트 전파를 막으면 됩니다.


앞의 예제를 확장해 아래와 같이 메뉴 항목 클릭하면 페이지를 이동하는 코드를 작성했을 때, "이벤트" 앵커 태그의 콜백 함수에 e.stopPropagation() 이 없으면 "이벤트" 링크 클릭시 "메뉴1" 의 콜백함수도 함께 실행됩니다.


<ul id="mainmenu">
    <li>메뉴1<a href="#" id="showevent">이벤트</a></li>
    <li>메뉴2</li>
    <li>메뉴3</li>
    <li>메뉴4</li>
</ul>

document.getElementById('mainmenu').addEventListener('click',function(e){
    let menulabel = e.target.tagName == 'A' ? e.target.parentNode.childNodes[0].textContent : \
 e.target.childNodes[0].textContent;
    switch(menulabel){
        case "메뉴1":
            break;
        case "메뉴2":
            break;
        case "메뉴3":
            break;
        case "메뉴4":
            break;
    }
    console.log(menulabel + ' 페이지 이동!');
  });
document.getElementById('showevent').addEventListener('click',function(e){
    console.log('이벤트 팝업 표시!');
    e.stopPropagation();
  });



반응형

닫기