Vue.js 에서 이벤트 핸들링

진성대·2023년 3월 30일
0

Vue.js

목록 보기
4/13

이벤트 핸들링

  • 이벤트 핸들링이란 발생할 수 있는 이벤트를 직접 개입하여 처리할 수 있다. 이벤트 핸들링을 하기 위해서는 이벤트 리스닝 과정을 거처야 한다.

이벤트 리스닝하기

  • 일반적으로 v-on디렉티브는 단축 문법으로 @기호를 사용하며, DOM 이벤트를 수신하고 트리거될 때, 사전에 정의해둔 JavaScript 코드를 실행할 수 있다.
    v-on:click="handler" 또는 줄여서 @click="handler"와 같이 사용할 수 있다.

인라인 핸들러

  • 인라인 핸들러는 일반적으로 아래와 같이 간단한 경우에 사용할 수 있다.
data() {
  return {
    count: 0
  }
}
<button @click="count++">1 추가 </button>
<p>숫자 값은: {{ count }} </p>

메서드 핸들러

  • 대부분의 이벤트 핸들러 논리는 복잡할 것이며, 인라인 핸들러에서는 실현 가능하지 않을 수 있다. 그래서 v-on은 컴포넌트의 매서드 이름이나 매서드를 가리키는 경로를 실핼할 수 있게 구현되어 있다.
data() {
  return {
    name: 'Vue.js'
  }
},
methods: {
   greet(event) {
     // `this`는 매서드가 활성화된 현재 인스턴스를 가리킨다.
     alert(`안녕 ${this.name}!`)
     // `event`는 네이티브 DOM 이벤트 객체입니다.
     if (event) {
       alert(event.target.tagName)
     }
   }
}
<!-- `greet`는 위에서 정의한 메서드의 이름입니다.--->
<button @click="greet">환영하기</button>
  • 메서드 핸들러는 이를 트리거하는 네이티브 DOM 이벤트 객체를자동으로 수신한다.
    -위의 예제에서 event.target.tagName을 통해 이벤트를 전달하는 엘리먼트에 접근할 수 있다.

메서드 vs 인라인 구분

  • 템플릿 컴파일러는 v-on값인 문자열이 유효한 JavaScript 식별자 또는 속성에 접근 가능한 경로인지 확인해 메서드 핸들러를 감지한다. 예를 들어 foo, foo.barfoo['bar']는 메서드 핸들러로 처리되는 반면, foo()count++는 인라인 핸들러로 처리된다.

인라인 핸들러에서 메서드 호출하기

  • 메서드 이름을 직접 바인딩하는 대신, 인라인 핸들러에서 메서드를 호출할 수도 있다. 이를 통해 네이티브 이벤트 객체 대신 사용자 지정 인자를 메서드에 전달할 수 있다.
methods: {
  say(message) {
    alert(message) {
  }
}
<button @click="say('안녕')">안녕이라고 말하기<button>
<button @click="say('잘가')">잘가라고 말하기<button>

인라인 핸들러에서 이벤트 객체 접근하기

  • 때로는 인라인 핸들러에서 네이티브 DOM 이벤트 객체에 접근해야 하는 경우도 있다. 특수한 키워드인 $event를 사용하여 메서드에 전달하거나 인라인 화살표 함수를 사용할 수 있다.
<!-- 특수한 키워드인 $evet 사용 -->
<button @click="warn('아직 양식을 제출할 수 없습니다.', $event)">
  제출하기
</button>

<!--인라인 화살표 함수 사용--->
<button @click="(event) => warn('아직 양식을 제출하 수 없습니다.', event)">
  제출하기 
</button>
methods: {
  warn(message, event) {
    // 이제 네이티브 이벤트 객체에 접근할 수 있습니다.
    if (event) {
      event.preventDefault()
    }
    alert(message) 
  }
}

이벤트 버블링

버블링이란?

버블링(bubbling)의 원리는 간단하다.
한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날때 까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.

ex =>
3개의 요소가 FORM > DIV > P 형태로 중첩된 구조가 있고 각 요소에 핸들러가 할당되어 있다.

<style>
  body* {
  margin: 10px;
  borader: 1px solid blue;
  }
</style>

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P
    </p>
  </div>
</form>

가장 안쪽의 <p>를 클릭하면 순서대로 다음과 같은 일이 벌어진다.
1. <p>에 할당된 onclick 핸들러가 동작한다.
2. 바깥의 <div>에 할당된 핸들러가 동작한다.
3. 그 바깥의 <form>에 할당된 핸들러가 동작한다.
4. document객체를 만날 때까지, 각 요소에 할당된 onclick핸들러가 동작한다.

이런 동작 방식 때문에 <p> 요소를 클릭하면 p -> div -> form 순서대로 창이 뜬다.
이런 흐름을 이벤트 버블링이라고 부른다. 이벤트가 제일 깊은 곳에 있는 요소에서 시작해 부모요소를 거슬러 올라가는 모습이 물속 거품과 닮았기 때문이다.

거의 모든 이벤트는 버블링 됩니다.
키워드는 ‘거의’ 입니다.
focus 이벤트와 같이 버블링 되지 않는 이벤트도 있습니다. 버블링 되지 않는 이벤트의 종류에 대해선 조금 후에 알아보겠습니다. 몇몇 이벤트를 제외하곤 대부분의 이벤트는 버블링 됩니다.

event.target

부모 요소의 핸들러는 이벤트가 정확히 어디서 발생했는지 등에 대한 정보를 얻을 수 있다.
이벤트가 발생한 가장 안쪽의 요소는 타깃(target)요소라고 불리고, event.target을 사용해 접근할 수 있다.
event.targetthis(=event.currentTarget)는 다음과 같은 차이점이 잇다.

  • event.target은 실제 이벤트가 시작된 '타깃'요소이며, 버블링이 진행되어도 변하지 않는다.
  • this는 '현재'요소로, 현재 실행 중인 핸들러가 할당된 요소를 참조한다.

버블링 중단하기

아래의 코드는 버블링 중단하기 예시중 하나이다

  • parent class로 정의된 div 파트는 child class로 정의된 div 파트의 부모 요소이다.
  • child 파트 부분을 클릭해서 event를 발생시키면 부모 이벤트도 발생한다. 하지만 이벤트 수식어를 통해서 child event만 발생하도록 버블리을 제어하였다.

이벤트 수식어

  • 이벤트 수식어는 이벤트 버블링을 제어할 수 있는 요소중에 하나이다.

  • 이벤트 핸들러 내에서 event.preventDefault() 또는 event.stopPropagation()을 호출하는 것은 매우 흔한일이다. 매서드 내에서 이 작업을 쉽게 수행할 수 있지만, 메서드가 DOM 이벤트에 대한 세부적인 처리 로직 없이 데이터 처리 로직만 잇다면 코드 유지보수에 더 좋다.

  • 이 문제를 해결하기 위해 v-on은 점(.)으로 시작하는 지시적 접미사인 이벤트 수식어를 제공한다.

  • .stop

  • .prevent

  • .self

  • .capture

  • .once

  • .passive : 기본적인 로직 처리와 화면 스크롤(이벤트)을 독립할 수 있게 해준다.

이벤트 캡처링

캡처링

  • 이벤트엔 버블링 외에도 캡처링(caputring)이라는 흐름이 존재한다.

표준 DOM 이벤트에서 정의한 이벤트 흐름은 3가지 단계를 가진다.
1. 캡처링 단계 - 이벤트가 하위 요소로 전파되는 단계
2. 타깃 단계 - 이벤트가 실제 타깃 요소에 전달되는 단계
3. 버블링 단계 - 이벤트가 상위 요소로 전파되는 단계

<td>를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파되고(캡처링 단계), 이벤트가 타깃 요소에 도착해 실행된 후(타깃 단계), 다시 위로 전파된다(버블링 단계). 이러한 과정을 통해 요소에 할당된 이벤트 핸들러가 호출된다.

<template>
  <div
    class="parent"
    @click.capture="handlerA"
    >
    <div
      class="child"
      @click="handlerB"></div>
  </div>  
</template>
<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>
<script>
import { valueToNode } from '@babel/types';

export default {
  methods: {
    handlerA() {
      console.log('A')
    },
    handlerB() {
      console.log('B')
    }
  }
}


자식 event를 클릭한다 하더라도 기존에 실행되던 동작 방식과 다르게 부모를 우선 호출하고 자식이 호출되는 것을 볼 수 있다

이는 이벤트 수식어인 capture를 이용하였다.

self 는 부모 자식 영역 상관없이 자기 영역부분만 event를 발생할 수 있도록 만들어 준다.

event.target : 클릭한 부분 (자식 부분)

event.currentTarget : 클릭한 부분의 부모 부분(부모 부분)

profile
신입 개발자

0개의 댓글