# 4. 이벤트처리

## 4. 이벤트 처리

다양한 이벤트처리를 Vue 기반으로 작성

### 이벤트 참고

이벤트 참고 문헌, 모질라, w3스쿨스

* [https://developer.mozilla.org/ko/docs/Web/Reference/Events](https://developer.mozilla.org/ko/docs/Web/Reference/Events)
* [https://www.w3schools.com/tags/ref\_eventattributes.asp](https://www.w3schools.com/tags/ref_eventattributes.asp)

### 4.1 인라인 이벤트처리

인라인 레벨에서 이벤트처리 하는 방법을 다룸

* v-on 디렉티브
  *   **Click 이벤트 처리**

      `v-on:click 또는 @click`

      ```javascript
      <div id="example" class="container layout1">
      <p>
        <input type="text" v-model="amount" class="form-control">
      </p>
      <p>
        <button id="deposit" v-on:click="balance += parseInt(amount)" class="btn btn-primary">예금</button>
        <button id="withdraw" @click="balance -= parseInt(amount)" class="btn btn-primary">인출</button>
      </p>
      <h3>계좌 잔고 : {{balance}}</h3>
      </div>
      ```

### 4.2 이벤트 핸들러 메서드

인라인에서 디렉티브에 바로 코딩하는 것이 아닌, `메서드를 미리 구현하여 참조` 위 예제에서 `v-on디렉티브 부분`을 `deposit와 withdraw메서드`에 참조하여 상세처리를 함

*   HTML

    ```markup
    <button id="deposit2" @click="deposit" class="btn btn-primary">예금</button>  
    <button id="withdraw2" @click="withdraw" class="btn btn-primary">인출</button>
    ```
*   JS

    ```javascript
    let vm2 = new Vue({
      el: '#example2',
      data: {
        amount: 0,
        balance: 0
      },
      methods: {
        deposit: function (e) {
          let amt = parseInt(this.amount);
          if (amt <= 0) {
            alert('0 보다 큰 값을 예금해야 합니다');
          } else {
            this.balance += amt;
          }
        },
        withdraw: function (e) {
          let amt = parseInt(this.amount);
          if (amt <= 0) {
            alert('0 보다 큰 값을 인출 할 수 없습니다.');
          } else if (amt > this.balance) {
            alert('잔고보다 많은 금액을 인출 할 수 없습니다.');
          } else {
            this.balance -= amt;
          }
        }
      }
    });
    ```

### 4.3 이벤트 객체

`function(e){...}`에서 **첫번째 파라미터는 이벤트 객체** W3C의 표준 DOM Event 모델을 그대로 따르며, 추가적인 속성을 다룬다. 따라서 **순수JS에서 사용하는 방법 그대로 이용 가능** 딱히 설명은 없고 정리된 글로, 책에서 정리한 표를 참고. 따로 사용예는 없다

자주 쓰는 target, currentTarget, defaultPrevented.. 등등 속성 정리되어 있음. 한번쭉 읽어보기

### 4.4 기본이벤트

**기본이벤트란** 태그특징에 의해 이벤트가 내장되어 있는 것.\
**ex) `<a>`의 `href`속성을 통한 `클릭이벤트`** **ex) `<form>`의 submit 버튼 -> `action`**  **ex) `<input type='text'>`의 `키이벤트`**

편하긴 하지만 때로는 이런 기본이벤트가 걸림돌이 되는 경우가 있어서 이를 중지 시킬수 있는 방법!!

* **`preventDefault함수`** 대상 이벤트의 실행을 중단시켜줍니다.
  *   **HTML**

      ```markup
      <div id="example3" v-on:contextmenu="ctxStop">
      <a href="https://facebook.com" @click="confirmFB">페이스북</a>
      </div>
      ```
  *   JS

      여기에서 ctxStop -> 해당div에 걸리분부분 context메뉴 관련 이벤트 중단(마우스 우클릭 같은..)

      ```javascript
      let vm3 = new Vue({
        el: '#example3',
        methods: {
          ctxStop: function (e) {
            console.log("ctx");
            e.preventDefault();
          },
          confirmFB: function (e) {
            console.log("faceFB");
            if (!confirm('페이스북으로 이동할까요?')) {
              e.preventDefault(); // e는 a태그, 취소 버튼 클릭시 : a태그의 이동을 중지시킴.
            }
          }
        }
      });
      ```
* 이벤트 수식어 v-on:contextmenu.**prevent** 처럼 명시하면 preventDefault가 적용됨.\
  매번 코딩하는 번거로움을 덜어주는 방식

```markup
<div id="example3" v-on:contextmenu.prevent>
  <a href="https://facebook.com" @click="confirmFB">페이스북</a>
</div>
```

### 4.5 이벤트 전파와 버블링

HTML문서의 이벤트 처리는 3단계로 구성.

*   1단계

    문서내의 요소 이벤트 발생, **HTML 문서 밖에서부터 이벤트를 발생시킨 HTML요소까지 포착**, `포착단계(capturing)`
*   2단계

    이벤트를 발생시킨 요소에 다다르면, **이벤트에 연결된 함수를 직접호출** 시킴, `이벤트 발생단계(raising)`
* 3단계 **이벤트가 발생한 요소로부터 상위요소로 거슬러 올라가면서 동일이벤트 호출**, `버블링단계(bubbling`
*   예시 inner(노랑색)를 클릭시 -> outer(아쿠아색)도 실행 (**버블링**) - `문제요소`\
    **inner 클릭시 -> outer부분에서** `cuurentTarget은 outer, target은 inner`

    | 확인             | inner click   | outer click   |
    | -------------- | ------------- | ------------- |
    | eventPhase     | 2(AT\_Target) | `3(BUBBLING)` |
    | cuurent target | inner         | `outer`       |
    | target         | inner         | inner         |
*   **HTML**

    ```markup
    <div id="example4">
    <div id="outer" @click="outerClick">
      <div id="inner" @click="innerClick"></div>
    </div>
    </div>
    ```
*   **JS**

    ```javascript
    let vm4 = new Vue({
      el: '#example4',
      methods: {
        outerClick: function (e) {
          console.log('### OUTER CLICK');
          console.log('Event Phase : ', e.eventPhase);
          console.log('Current Target : ', e.currentTarget);
          console.log('Target : ', e.target);
        },
        innerClick: function (e) {
          console.log('### INNER CLICK');
          console.log('Event Phase : ', e.eventPhase);
          console.log('Current Target : ', e.currentTarget);
          console.log('Target : ', e.target);
        }
      }
    });
    ```
* **해결 방법**
  *   **이벤트 객체의 stopPropagation() 호출**

      ```javascript
      let vm4 = new Vue({
        el: '#example4',
        methods: {
          outerClick: function (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: function (e) {
            console.log('### INNER CLICK');
            console.log('Event Phase : ', e.eventPhase);
            console.log('Current Target : ', e.currentTarget);
            console.log('Target : ', e.target);
            e.stopPropagation();
          }
        }
      });
      ```
  *   **이벤트 수식어**\
      `.stop`을 추가하면 stopPropagation이 자동 추가됨

      ```markup
      <div id="example4">
        <div id="outer" @click.stop="outerClick">
          <div id="inner" @click.stop="innerClick"></div>
        </div>
      </div>
      ```

      * **.stop** : `이벤트 버블링 중단`
      * **.capture** : `캡쳐링단계(1단계)`에서만 이벤트 발생
      * **.self** : `라이징단계(2단계)`에서만 이벤트 발생
      *   체인방식 가능 : `@click.capture.stop` 처럼 사용 가능

          ```markup
          <div id="example4">
            <div id="outer" @click.capture.stop="outerClick">
              <div id="inner" @click.stop="innerClick"></div>
            </div>
          </div>
          ```

          좀 다르게 동작, **inner 클릭시** 포착단계에서 outer만 실행되고 전파를 중지하게 되므로 `inner 실행이 안됨`

### 4.6 이벤트 수식어

**활용시 단순 코딩을 줄일 수 있다.**\
앞에서 살펴본 **.prevent, .stop, .capture, .self** 외 살펴보기

*   **once 수식어**

    이벤트를 한번만 발생

```markup
<button id="create" @click.once="specialEvent" class="btn btn-primary">계좌개설 10000원 이벤트</button>
```

*   **키코드 수식어**

    **키보드 클릭시** 키에 대한 `고유 키코드` 값을 가질때만 이벤트 발생\
    **ex) v-on:keyup.13="함수"**, 13(엔터키)

    *   HTML

        ```markup
        <div id="example6" v-cloak>
        <p>
          이름 : <input type="text" v-model="name" v-on:keyup="search" placeholder="두자 이상 입력">
          <!--    이름 : <input type="text" v-model="name" v-on:keyup.13="search" placeholder="두자 이상 입력">-->
        </p>
        <table id="list">
          <thead>
          <tr>
            <th>번호</th>
            <th>이름</th>
            <th>전화번호</th>
            <th>주소</th>
          </tr>
          </thead>
          <tbody id="contracts">
          <tr v-for="contact in contactlist">
            <td>{{contact.no}}</td>
            <td>{{contact.name}}</td>
            <td>{{contact.tel}}</td>
            <td>{{contact.address}}</td>
          </tr>
          </tbody>
        </table>
        <div v-show="isProcessing === true">조회중</div>
        </div>
        ```
    *   JS

        ```javascript
        let vm6 = new Vue({
          el: '#example6',
          data: {
            name: '',
            isProcessing: false,
            contactlist: []
          },
          methods: {
            search: function (e) {
              if (e.keyCode === 13) {     // 이부분 수식어로 대체가능
                let val = e.target.value;
                if (val.length >= 2) {
                  this.fetchContacts();
                } else {
                  this.contactlist = [];
                }
              }
            },
            fetchContacts: function () {
              this.contactlist = [];
              this.isProcessing = true;
              var url = "http://sample.bmaster.kro.kr/contacts_long/search/" + this.name;
              var vm = this;
              fetch(url)
                  .then(function (res) {
                    return res.json();
                  })
                  .then(function (json) {
                    vm.contactlist = json;
                    vm.isProcessing = false;
                  })
                  .catch(function (err) {
                    console.log('parsing failed', err);
                    vm.contactlist = [];
                    vm.isProcessing = false;
                  });
            }
          }
        });
        ```

    **키코드 값을 일일이 기억할 수 없으므로**, `몇가지에 대해 Vue.js에서 수식어로 별칭을 제공함`

    * ex) .enter, .tab, .delete, .esc, .space 등등.. 책 참고
    *   여러개 결합사용 가능 : `@keyup.ctrl.67='copy'`, **ctrl+c 키 입력시**

        ```markup
        이름 : <input type="text" v-model="name" @keyup.ctrl.67="copy" placeholder="테스트copy">
        ```
*   **마우스 버튼 수식어**

    **.left(왼), .right(오), .middle(휠)**

    *   HTML

        \`\`\`html

        &#x20; Left Click : 왼쪽으로 \


        &#x20; Right Click : 오른쪽으로&#x20;

        \</div>

        \<img src="[http://sample.bmaster.kro.kr/img/foot.jpg](http://sample.bmaster.kro.kr/img/foot.jpg)"

        ```
        v-bind:style="{position:'absolute', left:pos.left+'px', top:pos.top+'px'}">
        ```

    \</div>

    ````
    - JS
    ```javascript
    let vm7 = new Vue({
      el: '#example7',
      data: {
        pos:{left:100, top: 100}
      },
      methods: {
        ctxStop: function (e) {
        },
        leftMouse: function (e) {
          if (this.pos.left > 30) {
            this.pos.left -= 30;
            console.log("Move Left!!");
          }
        },
        rightMouse: function (e) {
          this.pos.left += 30;
          console.log("Move Right");
        }
      }
    });
    ````
