Front-end/Vue

Vue.js) 이벤트 핸들링 - 이벤트 수식어

ljs981026 2024. 7. 26. 15:10

1. 이벤트 수식어

이벤트 수식어는 이벤트 핸들러의 동작을 쉽게 변경할 수 있도록 도와주는 유용한 기능입니다. 이벤트 수식어를 사용하면 이벤트가 발생했을 때 기본 동작을 막거나, 이벤트 전파를 막는 등의 작업을 간단하게 수행할 수 있습니다.

 

2. prevent

  • 이벤트의 기본 동작을 방지합니다.
<template>
	<a href="https://google.com" target="_blank" @click="handler1"> Google1 </a>
	<a href="https://google.com" target="_blank" @click.prevent="handler2">
		Google2
	</a>
</template>

<script>
export default {
	methods: {
		handler1(e) {
			e.preventDefault();
			console.log('ABC!');
		},
		handler2() {
			console.log('ABC!');
		},
	},
};
</script>

<style scoped>
a {
	display: block;
}
</style>

이벤트 객체를 받아와 e.preventDefault() 혹은 엘리먼트 디렉티브에 @["event"]. prevent="" 와 같이 표현을 하면 새창이 뜨지 않고 console로 출력하는 "ABC!"만 출력됨을 알 수 있습니다.

 

3. once 

  • 이벤트가 한 번만 발생합니다.
<template>
	<a href="https://google.com" target="_blank" @click.once="handler1"> Google1 </a>
</template>

<script>
export default {
	methods: {
		handler1() {
			console.log('ABC!');
		},
	},
};
</script>

<style scoped>
a {
	display: block;
}
</style>

a태그를 클릭 시 새창은 계속 뜨지만 handler1()의 로직은 단 한 번만 작동하는 것을 알 수 있습니다.

 

4. stop

  • 이벤트 버블링을 방지합니다.
<template>
	<div class="parent" @click="handlerA">
		<div class="child" @click="handlerB">B</div>
		<div class="child2" @click="handlerC">C</div>
		<div class="child3" @click.stop="handlerD">D</div>
	</div>
</template>

<script>
export default {
	methods: {
		handlerA() {
			console.log('A');
		},
		handlerB() {
			console.log('B');
		},
		handlerC(e) {
			e.stopPropagation();
			console.log('C');
		},
		handlerD() {
			console.log('D');
		},
	},
};
</script>

<style scoped lang="scss">
.parent {
	display: flex;
	justify-content: space-between;
	width: 200px;
	height: 100px;
	background-color: royalblue;
	margin: 10px;
	padding: 10px;
	.child {
		width: 100%;
		height: 100px;
		background-color: gold;
	}
	.child2 {
		width: 100%;
		height: 100px;
		background-color: orange;
	}
	.child3 {
		width: 100%;
		height: 100px;
		background-color: greenyellow;
	}
}
</style>
B
C
D

div 클래스인 parent가 자식 엘리먼트들 child, child2, child3 클래스들을 감싸고 있는 형태입니다. parent 요소를 클릭하면 console엔 'A'만 출력하지만 child 클래스를 클릭하면 console에 부모 요소까지 포함한 'A'와 'B'가 출력됩니다. 이처럼 자식 엘리먼트에 지정한 이벤트 함수가 부모 엘리먼트에까지 영향을 미치는 것을 이벤트 버블링이라고 합니다. 이를 방지하기 위해서 child2, child3 클래스에. stop이나 메서드 부분에 event 객체를 받아와 e.stopPropagation()을 추가해 주면 이벤트 버블링이 방지됩니다.

 

5. capture

  • 이벤트를 캡처링 단계에서 처리합니다. 즉, 이벤트 버블링은 자식 엘리먼트 -> 부모 엘리먼트 순으로 영향을 주지만 caputre을 사용하면 부모 엘리먼트 -> 자식엘리먼트 역순으로 처리되도록 해줍니다.
<template>
	<div class="parent" @click="handlerA">
		<div class="child" @click="handlerB">미적용</div>
	</div>
	<div class="parent" @click.capture="handlerA">
		<div class="child2" @click="handlerB">적용</div>
	</div>
</template>

<script>
export default {
	methods: {
		handlerA() {
			console.log('A');
		},
		handlerB() {
			console.log('B');
		},
	},
};
</script>

<style scoped lang="scss">
.parent {
	display: flex;
	justify-content: space-between;
	width: 200px;
	height: 100px;
	background-color: royalblue;
	margin: 10px;
	padding: 10px;
	.child {
		width: 95px;
		height: 100px;
		background-color: gold;
	}
	.child2 {
		width: 95px;
		height: 100px;
		background-color: greenyellow;
	}
}
</style>
미적용
적용

 

 

capture를 적용한 부모 엘리먼트와 적용하지 않은 엘리먼트 두개를 생성하여 비교해 보겠습니다. 먼저 적용을 하지 않은 부분의 자식 엘리먼트를 클릭하면 console에 B -> A 순으로 출력되지만 적용이 된 부분의 자식 엘리먼트를 클릭하면 console에 A -> B 순으로 출력됩니다.

 

 

6. self

  • 이벤트가 해당 요소에서만 동작할 때 핸들러가 실행됩니다.
<template>
	<div class="parent-wrap">
		<div>
			<span>self 미적용</span>
			<div class="parent" @click="handlerA">
				<div class="child"></div>
			</div>
		</div>
		<div>
			<span>self 적용</span>
			<div class="parent" @click.self="handlerB">
				<div class="child"></div>
			</div>
		</div>
	</div>
</template>

<script>
export default {
	methods: {
		handlerA(e) {
			console.log(e.target.className);
			console.log(e.currentTarget.className);
		},
		handlerB(e) {
			console.log(e.target.className);
			console.log(e.currentTarget.className);
		},
	},
};
</script>

<style scoped lang="scss">
.parent-wrap {
	display: flex;
	.parent {
		width: 200px;
		height: 100px;
		background-color: royalblue;
		margin: 10px;
		padding: 10px;
		.child {
			width: 100px;
			height: 100px;
			background-color: gold;
		}
	}
}
</style>
self 미적용
self 적용

self를 미적용한 엘리먼트는 부모 또는 자식 엘리먼트를 클릭하여도 이벤트가 발생됩니다. 하지만 부모 엘리먼트를 클릭하면 console에 'parent, parent'가 출력되고 자식 엘리먼트를 클릭하면 console에 'child, parent'가 출력됩니다. 

반면에 self를 적용한 엘리먼트는 self를 적용한 부모 엘리먼트 부분만 이벤트가 발생합니다. console에는 'parent, parent'가 출력됩니다. 따라서 self는 event 객체의 target과 currentTarget이 같아야 이벤트가 동작됩니다.

*event.target: 실제로 클릭이 된 요소를 지칭합니다.

*event.currentTarget: currentTarget이 실행된 해당하는 함수에 연결이 되어져 있는 이벤트에 해당하는 요소를 지칭합니다.

 

7. passive

  • 기본 동작을 실행할 때 이벤트 핸들러가 비동기적으로 실행되도록 합니다. 주로 스크롤 이벤트에서 성능을 개선하기 위해 사용합니다.
<template>
	<div>
		<div class="parent" @wheel.passive="handler">
			<div class="child"></div>
		</div>
	</div>
</template>

<script>
export default {
	methods: {
		handler(e) {
			for (let i = 0; i < 10000; i++) {
				console.log(e);
			}
		},
	},
};
</script>

<style scoped lang="scss">
.parent {
	width: 200px;
	height: 100px;
	background-color: royalblue;
	margin: 10px;
	padding: 10px;
	overflow: auto;
	.child {
		width: 100px;
		height: 2000px;
		background-color: gold;
	}
}
</style>

passive 옵션은 기본적인 로직의 처리와 화면에 위아래 스크롤을 완전히 독립시켜 줄 수 있습니다. 그래서 passive 옵션을 주지 않고 스크롤을 내리면 버벅거림이 있지만 passive 옵션을 주고 스크롤을 내리면 부드럽게 내려가는 것을 확인할 수 있습니다. 이러한 기능으로 사용자들 입장에서는 화면이 부드럽게 스크롤이 되지만 스크롤이 될 때 처리해야 되는 로직은 따로 내부적으로 돌아가고 있는 구조를 구현할 수 있습니다.

 

참고 문서:

https://v2.ko.vuejs.org/v2/guide/events.html#%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%88%98%EC%8B%9D%EC%96%B4