JavaScript 이벤트 전파와 중단하기
JavaScript의 이벤트 전파와 중단 방법에 대해 알아보겠습니다.
1. Event 전파
브라우저에서 요소에 대한 event가 발생하면 해당 요소에 할당된 handler가 동작하게 되는데, 이 때 handler가 동작하면서 다음과 같이 Bubbling과 Capturing이 발생하게 됩니다.
1.1. Bubbling
bubbling은 특정 요소에서 event가 발생했을 때 상위 요소로 event가 전파되는 것을 의미합니다.
<html>
<body>
<div>
<button>click</button>
</div>
</body>
</html>
<script>
const elements = document.querySelectorAll('*');
elements.forEach((item) => {
item.addEventListener(
'click',
(event) => {
console.log('bubbling: ' + item.tagName);
},
{ capture: false }
);
});
</script>
위의 코드를 실행하고 <button>을 클릭하면 다음과 같이 콘솔 로그가 출력됩니다.
브라우저에서 event bubbling이 발생하면 최상위 요소인 document 객체에 도달할 때까지 상위로 이벤트가 전파됩니다. 이벤트가 전파되는 것을 확인하려면 위와 같이 태그에 handler가 추가되어 있어야 합니다. 또한 focus와 같이 bubbling이 발생하지 않는 이벤트들을 제외하고 대부분의 이벤트들은 bubbling이 발생합니다.
1.2. Capturing
capturing은 특정 요소에서 event가 발생했을 때 bubbling과 반대로 하위 요소로 event가 전파되는 것을 의미합니다.
<html>
<body>
<div>
<button>click</button>
</div>
</body>
</html>
<script>
const elements = document.querySelectorAll('*');
elements.forEach((item) => {
item.addEventListener(
'click',
(event) => {
console.log('capturing: ' + item.tagName);
},
{ capture: true }
);
});
</script>
위의 코드를 실행하고 <button>을 클릭하면 다음과 같이 콘솔 로그가 출력됩니다.
브라우저에서 event capturing이 발생하면 최상위 요소인 document 객체로부터 이벤트가 발생한 target 요소에 도달할 때까지 하위로 이벤트가 전파됩니다. capturing을 확인하기 위해서는 addEventListener()의 capture 옵션을 true로 설정해주면 됩니다.
1.3. event.target
브라우저에서 event가 발생했을 때 실제로 이벤트가 발생한 요소를 target 요소라고 합니다. event.target을 이용하여 접근할 수 있습니다.
<form id="form">form
<div>div
<p>p</p>
</div>
</form>
<script>
form.onclick = function(event) {
console.log('event.target.tagName', event.target.tagName);
console.log('this', this.tagName);
};
</script>
위의 코드를 실행하고 각 요소를 순서대로 클릭하면 다음과 같이 콘솔 로그가 출력됩니다.
event.target과 같이 확인한 this는 event.currentTarget과 동일하며 다음과 같은 차이점이 있습니다.
- event.target
실제로 event가 발생한 요소입니다. - this
현재 실행중인 handler가 할당된 요소입니다.
여기서는 form 안의 모든 요소에서 발생하는 이벤트를 잡아냅니다.
따라서 form 안에서 이벤트가 발생하면 bubbling이 발생하여 handler가 실행됩니다.
2. Event 중단
2.1. event.preventDefault()
event.preventDeafult()는 이벤트의 기본 동작을 중단시킵니다. html의 태그들 중에서 <a> 태그 같은 경우엔 지정한 링크로 페이지를 이동하는데 이러한 동작을 중단시킵니다.
<form onclick="console.log('form');">form
<div onclick="console.log('div');">div
<a href="https://freestrokes.tistory.com" id="a_tag">click</a>
</div>
</form>
<script>
const aTagElement = document.querySelector('#a_tag');
aTagElement.addEventListener('click', (event) => {
event.preventDefault();
console.log('event.target.tagName', event.target.tagName);
});
</script>
위의 코드를 실행하고 <a> 태그를 클릭하면 다음과 같이 콘솔 로그가 출력됩니다.
<a> 태그의 href 속성이 중단되어 페이지 이동이 일어나지 않지만, bubbling이 발생하여 이벤트 전파는 그대로 일어나는 것을 확인할 수 있습니다.
2.2. event.stopPropagation()
event.stopPropagation()은 이벤트 전파를 중단시킵니다. 따라서 bubbling 이나 capturing을 막아야하는 경우에 사용합니다.
<form onclick="console.log('form');">form
<div onclick="console.log('div');">div
<a href="https://freestrokes.tistory.com" id="a_tag">click</a>
</div>
</form>
<script>
const aTagElement = document.querySelector('#a_tag');
aTagElement.addEventListener('click', (event) => {
event.stopPropagation();
console.log('event.target.tagName', event.target.tagName);
});
</script>
위의 코드를 실행하고 <a> 태그를 클릭하면 다음과 같이 콘솔 로그가 출력되고 <a> 태그의 기본 동작으로 인하여 페이지 이동이 일어납니다.
이벤트 전파가 중단되어 버블링이 발생하지 않지만 target의 기본 동작과 다른 event handler의 동작까지 막아주지는 않습니다. 따라서 target에 다른 event handler가 걸려있거나 기본 동작이 있는 경우에는 그대로 동작하게 됩니다.
2.3. event.stopImmediatePropagation()
event.stopImmediatePropagation()은 이벤트 전파를 중단시키고 다른 event handler의 동작도 막아줍니다. 따라서 target이 기본 동작을 가지고 있는 경우에 기본 동작만 일어나게 됩니다.
<form onclick="console.log('form');">form
<div onclick="console.log('div');">div
<a href="https://freestrokes.tistory.com" id="a_tag">click</a>
</div>
</form>
<script>
const aTagElement = document.querySelector('#a_tag');
// 1st Event
aTagElement.addEventListener('click', (event) => {
event.stopImmediatePropagation();
console.log('click event 1');
});
// 2nd Event
aTagElement.addEventListener('click', (event) => {
console.log('click event 2');
});
</script>
위의 코드를 실행하고 <a> 태그를 클릭하면 이벤트 전파 중단과 함께 첫번째 event handler만 동작하고 다른 handler는 동작하지 않는 것을 확인할 수 있습니다. <a> 태그의 기본 동작은 중단되지 않기 때문에 페이지 이동은 그대로 일어나게 됩니다.
즉, event.stopImmediatePropagation()은 이벤트 전파를 중단하고 같은 target에 걸린 다른 handler의 동작도 중단시킵니다.
2.4. return false
return false는 onclick handler에 사용했을 경우에 대하여 event.preventDefault()를 사용한 것과 같은 동작을 합니다. 따라서 이벤트 전파는 발생하지만 기본 동작은 중단되게 됩니다.
다음 코드를 실행하고 <a> 태그를 클릭하면 페이지 이동이 발생하지 않게 됩니다.
<form onclick="console.log('form');">form
<div onclick="console.log('div');">div
<a href="https://freestrokes.tistory.com" onclick="return false;">click</a>
</div>
</form>
하지만 아래처럼 event listener에 사용하면 기본 동작이 일어나 페이지 이동이 발생하게 됩니다.
<form onclick="console.log('form');">form
<div onclick="console.log('div');">div
<a href="https://freestrokes.tistory.com" id="a_tag">click</a>
</div>
</form>
<script>
const aTagElement = document.querySelector('#a_tag');
// 1st Event
aTagElement.addEventListener('click', (event) => {
console.log('click event 1');
return false;
});
// 2nd Event
aTagElement.addEventListener('click', (event) => {
console.log('click event 2');
return false;
});
</script>
event handler 동작과 함께 이벤트 전파도 발생하게 됩니다.
반면에, jQuery를 사용한 경우엔 차이가 있습니다. jQuery event handler에 return false를 사용하면 event.preventDefault()와 event.stopPropagation()을 모두 사용한 것처럼 동작하게 됩니다.
<form id="form_tag">form
<div id="div_tag">div
<a href="https://freestrokes.tistory.com" id="a_tag">click</a>
</div>
</form>
<script>
$('#a_tag').on('click', function() {
console.log('click a tag');
return false;
});
$('#div_tag').on('click', function() {
console.log('click div tag');
});
$('#form_tag').on('click', function() {
console.log('click form tag');
});
</script>
위의 코드를 실행하고 <a> 태그를 클릭하면 이벤트 전파가 중단되고 기본 동작도 중단되어 페이지 이동이 일어나지 않는 것을 확인할 수 있습니다.
이상으로 JavaScript의 이벤트 전파와 중단 방법에 대해 알아봤습니다.
※ Reference
- javascript.plainenglish.io, The 3 Phases of Event Propagation in JavaScript Explained, https://javascript.plainenglish.io/the-3-phases-of-event-propagation-explained-f76348b5343f
- ko.javascript.info, 버블링과 캡처링, https://ko.javascript.info/bubbling-and-capturing
- programmingsummaries.tistory.com, [JavaScript] JavaScript에서 이벤트 전파를 중단하는 네가지 방법, https://programmingsummaries.tistory.com/313
- joshua1988.github.io, 이벤트 버블링, 이벤트 캡처 그리고 이벤트 위임까지, https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/
- hanjungv.github.io, (JS) addEventListener() vs onclick(), https://hanjungv.github.io/2018-01-21-1_JS_onclick_vs_addEventListener/