
한 번에 끝내는 프론트엔드 개발 초격차 패키지 Online를 들으며 정리한 내용입니다.
이벤트 핸들러 내부에서 event.preventDefault() 또는 event.stopPropagation()를 호출하는 것은 매우 보편적인 일입니다. 메소드 내에서 쉽게 이 작업을 할 수 있지만, DOM 이벤트 세부 사항을 처리하는 대신 데이터 로직에 대한 메소드만 사용할 수 있으면 더 좋습니다.
이 문제를 해결하기 위하여, Vue는 v-on이벤트에 이벤트 수식어를 제공합니다. 수식어는 점으로 된 접미사 입니다.
.stop
.prevent
.capture
.self
.once
.passive
아래와 같이 체이닝으로 작성할 수도 있습니다.
@click.prevent.once="handler"> //html의 기본 동작도 제어하고 단 1회만 실행될 수 있도록 합니다.
<template>
<a
href="https://naver.com"
target="_blank"
@click="handler">
Naver</a>
</template>
<script>
export default {
methods: {
handler(event) {
event.preventDefault()
console.log('ABC!')
}
}
}
</script>
preventDefault메소드를 실행했기 때문에, a태그를 눌러도 네이버 페이지로 이동하지 않습니다.
이렇게 event.preventDeault() 메소드를 이용하면 이미 html에 있는 기능을 동작시키지 않고, 메소드만 실행하게 만들 수 있습니다.
앞서 본 event.preventDefault메소드 대신 vue.js의 prevent이벤트 수식어를 이용해 조금 더 단순화해 사용할 수 있습니다.
<template>
<a
href="https://naver.com"
target="_blank"
@click.prevent="handler">
Naver</a>
</template>
<script>
export default {
methods: {
handler() {
console.log('ABC!')
}
}
}
</script>
특정 이벤트를 발생했을 때 해당하는 메소드를 단 1회만 실행해줍니다.
아래 코드를 싱행한 후 parent 영역 클릭시 콘솔창에는 A가 출력이 되고 child 영역 클릭시 콘솔창에는 B 다음으로 A가 출력되게 됩니다.
자식 요소를 클릭하면 결국 이를 포함하고 있는 부모 요소를 클릭하는 것과 마찬가지로 위의 요소를 타고 타고 올라가서 전파를 한다는 의미로 이를 이벤트 버블링이라고 부릅니다.
<template>
<div
class="parent"
@click="handlerA">
<div
class="child"
@click="handlerB"></div>
</div>
</template>
<script>
export default {
methods: {
handlerA() {
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
<style scoped lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
.child {
width: 100px;
height: 100px;
background-color: orange;
}
}
</style>
만약, child만 클릭하고 parents는 동작하지 않도록 하고 싶다면 event.stopPropgation을 이용해 이벤트 버블링을 방지 할 수 있습니다.
<script>
export default {
methods: {
handlerA() {
console.log('A')
},
handlerB(event) {
event.stopPropagation()
console.log('B')
}
}
}
</script>
<template>
<div
class="parent"
@click="handlerA">
<div
class="child"
@click.stop="handlerB"></div>
</div>
</template>
이벤트 캡쳐링은 위의 이벤트 버블링과 반대되는 개념으로 바로 부모 요소에서 자식 요소로 내려오는 개념입니다.
기존 예시 코드에서 자식 요소를 클릭했을 때 부모요소의 메소드가 먼저 실행되고 자식 요소의 메소드가 실행되게 하고 싶다면, .capture라는 이벤트 수식어를 적용하면 됩니다.
<template>
<div
class="parent"
@click.capture="handlerA">
<div
class="child"
@click="handlerB"></div>
</div>
</template>
<template>
<div
class="parent"
@click.self="handlerA">
<div
class="child"></div>
</div>
</template>
클릭 이벤트를 부모요소에만 남긴 후 자식요소를 클릭해보면, 자식요소 부분을 아무리 클릭해도 콘솔창에는 아무런 내용이 확인되지 않는 것을 확인할 수 있습니다.
하지만 자식요소를 포함하고 있지 않는 부모 요소 부분을 클릭하면 콘솔창에 A가 정상적으로 출력되는 것을 확인할 수 있습니다.
self란 self라는 수식어가 부터 있는 그 클릭이라는 이벤트가 붙어 있는 그 parent 영역을 정확하게 클릭했을 때 동작하게 만들어주는 것입니다.
자식 요소가 부모요소에 포함되고 있기 때문에 부모요소의 메소드가 실행되게 됩니다.
조금 더 자세히 살펴보기 위해 event.target()메소드와 event.currentTarget()메소드를 살펴보도록 하겠습니다.
target은 실제로 클릭이 된 요소를 지칭하며, currentTarget은 실행된 해당하는 함수에 연결이 되어져 있는 그 이벤트의 해당하는 요소가 출력되게 됩니다.
<script>
export default {
methods: {
handlerA(event) {
console.log(event.target)
console.log(event.currentTarget)
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
오렌지 색 영역을 클릭해보면, 아래와 같이 event.target은 자식요소에 해당하는 부분이, event.currentTarget의 경우 부모요소에 해당하는 부분이 출력되었습니다.

하지만 파란색 영역을 클릭해보면, event.target과 event.currentTarget 모두 부모요소에 해당하는 부분이 출력되는 것을 확인할 수 있습니다.
self이벤트 수식어를 이용하면 자식 요소를 클랙해도 부모요소의 메소드가 실행되지 않게 됩니다.
실제로 클릭된 부분과 그 클릭이라는 이벤트가 연결된 해당하는 요소가 같은 경우에만 그 연결된 메소드가 실행이 되게 됩니다.
로직 처리에 부하를 주는 코드를 작성해보았습니다. 화면의 구현과 이에 해당하는 로직이 브라우저가 동시에 처리하기 때문에 로직이 복잡하고 양이 많다면 그만큼 부하가 발생하게 됩니다.
<template>
<div
class="parent"
@wheel="handler">
<div class="child"></div>
</div>
</template>
<script>
export default {
methods: {
handler(event) {
for(let i=0; i<10000; i+=1){
console.log(event)
}
}
}
}
</script>
<style scoped lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
overflow: auto; //parent요소보다 넘치는 경우 스크롤생성
.child {
width: 100px;
height: 2000px;
background-color: orange;
}
}
</style>
.passive라는 이벤트 수식어는 로직의 처리와 화면의 동작을 완전히 독립시켜 주게 됩니다. 이를 통해 부하를 줄일 수 있게 되는 것입니다. 화면은 부드러워보이지만, 로직은 내부적으로 따로 돌아가고 있는 구조가 되는 것입니다.