728x90
★ 이벤트 처리
- 동적 UI는 HTML 요소에서 발생하는 이벤트 처리를 통해 구현되는 경우가 많다.
- Vue의 이벤트는 HTML, 자바스크립트에서 사용하는 이벤트를 준용해서 사용하기 때문이다.
1. 인라인 이벤트 처리
- Vue에서의 이벤트 처리는 아래와 같이 주로 사용한다.
v-on:[이벤트 이름]="표현식"
- 가장 많이 쓰이는 click 이벤트 예
- v-on 디렉티브는 @로 줄여 쓸 수도 있다.
v-onclick="test($event)"
@click="test($event)"
<body>
<div id="app">
금액 : <input type="text" v-model.number="amount" /><br/>
<button v-on:click="balance += parseInt(amount)">입금</button>
<button v-on:click="balance -= parseInt(amount)">출금</button>
<br />
<h3>계좌 잔고 : {{ balance }}</h3>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
data() {
return { amount : 0, balance : 0 };
}
}).mount("#app")
</script>
</body>
2. 이벤트 핸들러 메서드
- Vue 인스턴스에 등록한 메서드를 이벤트 처리 함수로 연결
- 이와 같이 복잡한 기능은 메서드를 미리 작성해두고, v-on 디렉티브로 참조해서 이벤트를 수행한다.
<body>
<div id="app">
금액 : <input type="text" v-model.number="amount"/><br/>
<button @click="deposit">입금</button>
<button @click="withdraw">인출</button>
<br/>
<h3>계좌 잔고 : {{ balance }} </h3>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
data() {
return { amount : 0, balance : 0}
},
methods : {
deposit() {
let amt = parseInt(this.amount);
if (amt <= 0) {
alert("0보다 큰 값을 예금해야 합니다.");
} else {
this.balance += amt;
}
},
withdraw() {
let amt = parseInt(this.amount);
if(amt <= 0) {
alert("0보다 큰 값을 인출할 수 있습니다.");
}else if (amt > this.balance ) {
alert("현재 있는 잔고보다 많은 금액을 인출 할 수 없습니다.");
}else {
this.balance -= amt;
}
}
}
}).mount("#app")
</script>
</body>
- 글자에 대한 이벤트 처리
- 하지만 이 메서드의 기능은 메서드로 처리하기에는 단 한줄의 코드만 실행한다.
<body>
<div id="app">
<input id="a" type="text" :value="name" @input="changeName">
<br />
입력하신 이름 : <span> {{ name }} </span>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
data() {
return { name : "" }
},
methods : {
changeName(e) {
this.name = e.target.value;
}
}
}).mount("#app")
</script>
</body>
- 상위 코드는 두가지 방법으로 개선할 수 있다.
- 1번째 방법 : 인라인 이벤트로 처리
<input id="a" type="text" :value="name" @input="this.name = $event.target.value">
- 2번째 방법 : 화살표 함수 사용
- 하지만, 이 방법은 논리 오류가 발생한다.
- 컴포넌트 인스턴스의 name이 아닌, window.name에 입력한 값이 할당이 된다.
<div id="app">
<input id="a" type="text" :value="name"
@input="(e) => this.name = e.target.value ">
<br />
입력하신 이름 : <span> {{ name }} </span>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
data() {
return { name : "" }
}
}).mount("#app")
</script>
</body>
- 하나의 이벤트에 대해 여러 이벤트 핸들러 메서드가 실행되도록 하는 방법
<button @click="change1($event), change2($event)">두 개의 핸들러 실행</button>
3. 이벤트 객체
- 메서드의 첫 번째 인자로 이벤트 객체 e를 전달받을 수 있다.
- e의 target 속성을 이용해 e.target.value 값을 전달받을 수 있다는 점
- 전체적인 이벤트 객체의 속성
- 3.1 이벤트 객체의 주요 공통 속성
속성명 | 설명 |
target | 이벤트가 발생한 HTML 요소를 리턴함. |
currentTarget | 이벤트 리스너가 이벤트를 발생시키는 HTML 요소를 리턴함. |
path | 배열값, 이벤트 발생 HTML 요소로부터 document, window 객체로까지 거슬러 올라가는 경로를 나타냄 |
bubbles | 현재의 이벤트가 버블링을 일으키는 이벤트인지 여부를 리턴함. |
cancelable | 기본 이벤트의 실행 취소할 수 있는지 여부를 리턴함. |
defaultPrevented | 기본 이벤트의 실행이 금지되었는지 여부를 나타냄. |
eventPhase | 이벤트 흐름의 단계를 나타냄. 1. 포착 2.이벤트 발생 3. 버블링 |
srcElement | IE에서 사용되던 속성으로 target과 동일한 속성 |
- 3.2 키보드 이벤트 관련 속성
속성명 | 설명 |
altKey | ALT 키가 눌려졌는지 여부를 나타냄(true/false) |
shiftKey | SHIFT 키가 눌려졌는지 여부를 나타냄(true/false) |
ctrlKey | CTRL 키가 눌려졌는지 여부를 나타냄(true/false) |
metakey | 메타키가 키가 눌려졌는지 여부를 나타냄(true/false), window에서는 window 키 |
key | 이벤트에 의해 나타나는 키의 값을 리턴함. 대소문자 구분 |
code | 이벤트를 발생시킨 키의 코드값을 리턴함. a를 눌렀을 때, "KeyA"를 리턴함. |
keyCode | 이벤트를 발생시킨 키보드의 고유 키코드 a,A는 65를 리턴함. |
charCode | keypress 이벤트가 발생될 때 Union 캐릭터 코드를 리턴함. |
- 3.3 마우스 이벤트 관련 속성
속성명 | 설명 |
altkey, shiftkey, ctrlkey, metakey |
키보드 이벤트 관련 속성 참조 |
button | 이벤트를 발생시킨 마우스 버튼 ( 0 : 마우스 왼쪽, 1 : 마우스 휠, 2 : 마우스 오른쪽) |
buttons | 마우스 이벤트가 발생한 후에 눌러져 있는 마우스 버튼의 값을 리턴함. (1: 마우스 왼쪽, 2: 마우스 오른쪽, 4: 마우스 휠, 8: 4번째 마우스, 16: 5번째 마우스) |
clientX, clientY | 마우스 이벤트가 일어났을 때의 영역상의 좌표 |
layerX, layerY | 마우스 이벤트가 발생한 HTML 요소 영역상에서 좌표(IE 이외의 브라우저 사용) |
offsetX, offsetY | 마우스 이벤트가 발생한 HTML 요소 영역상에서 좌표(IE 브라우저 사용) |
pageX, pageY | 마우스 이벤트가 일어났을 때의 HTML 문서 영역상의 좌표 |
screenX, screenY | 마우스 이벤트가 일어났을 때의 모니터 화면 영역상의 좌표 |
- 3.4 이벤트 객체의 주요 메서드
속성명 | 설명 |
preventDefault() | 기본 이벤트의 자동 실행을 금지시킴 |
stopPropagation() | 이벤트의 전파를 중단시킴 |
4. 기본 이벤트
- HTML 문서나, 요소에 어떤 기능을 실행하도록 이미 정의되어 있는 이벤트를 기본 이벤트라고 부른다.
- preventDefault() : 기본 이벤트를 중지시킬 수 있는 방법
- ctxStop 메서드 : 무조건 preventDefault() 메서드를 호출한다.
<body>
<div id="app">
<div @contextmenu="ctxStop"
style="position:absolute; top:5px; right:5px; bottom:5px; left:5px;">
<a href="https://facebook.com" @click="confirmFB">페이스북</a>
</div>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
methods : {
ctxStop(e) {
e.preventDefault();
},
confirmFB(e) {
if (!confirm("페이스북으로 이동할까요?")) {
e.preventDefault();
}
}
}
}).mount("#app")
</script>
</body>
- 하지만, 개발자가 이벤트에 의해 호출되는 메서드에 매번 e.preventDefault()를 작성하도록 신경쓰는 것은 쉽지 않다.
- 이때 사용하는 것이 이벤트 수식어
- 이렇게 이벤트명 뒤쪽에 .prevent와 같이 첨부하면 된다.
<div id="app">
<div @contextmenu.prevent
style="position:absolute; top:5px; right:5px; bottom:5px; left:5px;">
<a href="https://facebook.com" @click="confirmFB">페이스북</a>
</div>
</div>
5. 이벤트 전파와 버블링
- HTML 문서의 이벤트 처리는 3단계를 거친다.
- 1단계 : 문서 내의 요소에서 이벤트가 발생했을 때 HTML 문서의 밖에서부터 이벤트를 발생시킨 HTML 요소까지 포착해 들어가는 이벤트 포착 단계
- 2단계 : 이벤트를 발생시킨 요소에 다다르면 요소의 이벤트에 연결된 함수를 직접 호출시키는 이벤트 발생 단계
- 3단계 : 이벤트가 발생한 요소로부터 상위 요소로 거슬러 올라가면서 동일한 이벤트를 호출시키는 버블링 단계
<body>
<div id="app">
<div id="outer" @click="outerClick">
<div id="inner" @click="innerClick"></div>
</div>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
methods : {
outerClick(e) {
console.log("## OUTER CLICK");
console.log("Event Phase : ", e.eventPhase);
console.log("Current Target : ", e.currentTarget);
console.log("Target : ", e.target);
},
innerClick(e) {
console.log("## INNER CLICK");
console.log("Event Phase : ", e.eventPhase);
console.log("Current Target : ", e.currentTarget);
console.log("Target : ", e.target);
}
}
}).mount("#app")
</script>
</body>
- 하지만, 위 사항은 이벤트 버블링에 의해 두 가지의 버튼이 같이 클릭 됨.
- 이러한, 이벤트 버블링을 막기 위해, stopPropagation() 메서드를 이용한다.
- e.stop : 이벤트 전파를 중단시킨다.
- e.capture : CAPTURING_PHASE 단계에서만 이벤트가 발생한다.
- e.self : RAISING_PHASE 단계일 때만 이벤트가 발생한다.
var vm = Vue.createApp({
name : "App",
methods : {
outerClick(e) {
console.log("## OUTER CLICK");
console.log("Event Phase : ", e.eventPhase);
console.log("Current Target : ", e.currentTarget);
console.log("Target : ", e.target);
e.stopPropagation();
},
innerClick(e) {
console.log("## INNER CLICK");
console.log("Event Phase : ", e.eventPhase);
console.log("Current Target : ", e.currentTarget);
console.log("Target : ", e.target);
e.stopPropagation();
}
}
}).mount("#app")
- 더 간결한 방법
<div id="app">
<div id="outer" @click.stop="outerClick">
<div id="inner" @click.stop="innerClick"></div>
</div>
</div>
- 만약, .prevent 수식어 등을 모두 이어 붙여서 사용한다면
<div id="example">
<div id="outer" @click.capture.stop="outerClick">
<div id="inner" @click.stop="innerClick"></div>
</div>
</div>
6. 이외의 다양한 이벤트 수식어
- 6-1. once 수식어
- 한 번만 이벤트를 발생시키고 이벤트 연결을 해제한다.
<body>
<div id="app">
금액 : <input type="text" v-model.number="amount"/> <br>
<button @click="balance += parseInt(amount)">입금</button>
<button @click="balance -= parseInt(amount)">출금</button>
<button @click.once="balance += 10000">계좌 개설 이벤트</button>
<br>
<h3>계좌 잔고 : {{ balance }}</h3>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
data() {
return { amount : 0, balance : 0 };
}
}).mount("#app")
</script>
</body>
- 6-2. 키코드 관련 수식어
- 키보드 관련 이벤트를 처리할 때 사용할 수 있는 수식어.
- 수식어를 통해 단순한 코드 작성을 줄일 수 있다.
<body>
<div id="app">
이름 : <input type="text" v-model.trim="name" @keyup="search"
placeholder="영문 두글자 이상을 입력하세요."/><br />
<ul>
<li v-for="c in contacts">{{c.name}} : {{c.tel}}</li>
</ul>
<div v-show="isLoding">검색중</div>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript" src="https://unpkg.com/axios"></script>
<script type="text/javascript" src="https://unpkg.com/lodash"></script>
<script type="text/javascript">
const BASEURL = "https://contactsvc.bmaster.kro.kr";
var vm = Vue.createApp({
name : "App",
data() {
return { name : "", contacts : [], isLoding : false}
},
methods : {
search(e) {
if (e.keyCode == 13) {
if (this.name.length >=2) {
this.fetchContacts();
} else {
this.contacts = [];
}
}
},
fetchContacts : _.debounce(function() {
this.isLoding = true;
axios.get(BASEURL + `/contacts_long/search/${this.name}`)
.then((response)=>{
this.isLoding = false;
this.contacts = response.data;
})
}, 300)
}
}).mount("#app")
</script>
</body>
- 위와 같은 방식으로 keyCode를 사용해도 되지만, 수식어를 사용하는 편이 훨씬 더 편리하다.
이름 : <input type="text" v-model.trim="name" @keyup.enter="search"/>
//위와 동일
<script>
//위와 동일
var vm = Vue.createApp({
methods : {
search() {
if(this.name.length >=2) {
this.fetchContacts();
} else {
this.contacts = [];
}
},
.......
}
}).mount("#app")
</script>
- .enter와 같은 키코드 이외에도 다양한 키코드 관련 수식어가 존재함.
.up | .down | .left | .right |
.enter | .tab | .delete | .esc |
.space | .ctrl | .alt | .shift |
.meta |
- 6-3. 마우스 관련 수식어
- 마우스 버튼 수식어를 사용해, 마우스 왼쪽, 오른쪽 버튼을 클릭해서 목록에서 선택
.left | .right | .middle |
- currentIndex 값과 v-for 디렉티브를 이용해 목록을 반복 렌더링할 때의 index 값이 같을 때만, 화살표 아이콘 모양을 출력하고, active 클래스가 주어지도록 v-if 디렉티브를 사용한다.
<head>
<meta charset="UTF8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>05-13</title>
<link rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
<link rel="stylesheet" href="https://unpkg.com/font-awesome@4.7.0/css/font-awesome.min.css">
</head>
<body>
<div id="app">
<div class="container"
style="position:absolute; top:0; left:0; bottom:0; right: 0;"
@contextmenu.prevent @click.left="if (currentIndex > 0) currentIndex--;"
@click.right="if (currentIndex < itemlist.length-1) currentIndex++;"
@click.ctrl.left="currentIndex=0"
@click.ctrl.right="currentIndex=itemlist.length-1">
<div>
- 왼쪽 버튼 : 위로 <br/>
- 오른쪽 버튼 : 아래로 <br>
- CTRL + 왼쪽 버튼 : 처음으로 <br />
- CTRL + 오른쪽 버튼 : 마지막으로 <br />
</div>
<hr />
<ul class="list-group">
<li class="list-group-item"
:class="index === currentIndex ? 'active' : ' ' "
v-for="(item, index) in itemlist" :key="item">
{{item}}
<span v-if="index === currentIndex"
className="float-right badge badge-secondary">
<i class="fa fa-arrow-left" aria-hidden="true"></i>
</span>
</li>
</ul>
</div>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name : "App",
data() {
return {
itemlist : ["Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9"],
currentIndex : 0,
};
}
}).mount("#app")
</script>
</body>
- 6-4. exact 수식어
- exact 수식어는 다른 시스템 수식어와 조합해 이벤트를 등록할 때 정확하게 일치하는 조합으로 이벤트가 일어나야 핸들러가 실행되도록 설정한다.
<body>
<div id="app">
<ul>
<li>마우스 왼쪽만 클릭 : 1씩 증가</li>
<li>CTRL+왼쪽 클릭 : 10씩 증가</li>
<li>CTRL+ALT+왼쪽 클릭 : 100씩 증가</li>
</ul>
<button @click="num=num+1" @click.ctrl="num=num+10" @click.ctrl.alt="num=num+100">
클릭하세요!
</button>
<br>
<h3>카운트 : {{num}}</h3>
</div>
<script type="text/javascript" src="https://unpkg.com/vue"></script>
<script type="text/javascript">
var vm = Vue.createApp({
name: "App",
data() {
return { num : 0 };
}
}).mount("#app");
</script>
</body>
- 이렇게 작성하게 되면, CTRL+ALT+왼쪽 을 누르게 되면 100이 증가되는 것이 아니라, 세가지가 모두 해당 됨으로, 111이 증가가 된다. 이럴때, exact 수식어를 사용하면 된다.
<button @click.exact="num=num+1" @click.ctrl.exact="num=num+10" @click.ctrl.alt.exact="num=num+100">
728x90
'Vue.js' 카테고리의 다른 글
Vue.js 3.0 STEP 5 - TodoList (0) | 2024.01.25 |
---|---|
Vue.js 3.0 STEP 4 - 스타일 적용 (0) | 2024.01.25 |
Vue.js 3.0 STEP 2 - Vue 인스턴스 (0) | 2024.01.18 |
Vue.js 3.0 STEP 1 - Vue.js 기초와 Template (0) | 2024.01.17 |
ES6 STEP 2 - 기본 문법 2 (0) | 2024.01.17 |