【Vue.js - 3】イベントとフォーム入力の受け取り

## 1. イベントハンドラ

```html:html
<!-- メソッドイベントハンドラ -->
<button v-on:click="handleClick">クリック</button>
<!-- 省略記述 -->
<button @click="handleClick">クリック</button>

<!-- インラインメソッドハンドラ -->
<button v-on:click="count++"></button>

<!-- 引数（元のイベントオブジェクトとスコープ内のitem） -->
<button v-on:click="handleClick($event, item)">クリック</button>
```

フォーム入力の取得
（v-modelを使用するとシンプルになるが、v-onを使用することで入力内容を確認してからデータに代入するといったフック処理ができる）

```html:html
<input v-bind:value="message" v-on:change="handleInput">
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js',
  },
  methods: {
    handleInput: function (event) {
      //代入前に何か処理を行う…
      this.message = event.target.value
    }
  }
})
```

 イベント修飾子

| 修飾子 | 作用 |
|:--|:--|
|.stop | event.stopPropagation()を呼ぶ | 
|.prevebt | event.preventDefault()を呼ぶ | 
|.capture | キャプチャモードでDOMイベントをハンドルする | 
|.self | イベントがこの要素から発火した時のみハンドラを呼び出す | 
|.native | コンポーネントのルート要素上のネイティブイベントをハンドルする | 
|.once | 最大1回ハンドラを呼び出す | 
|.passive | {passive:ture}でDOMイベントをハンドルする | 

 クリックイベントの修飾子

| 修飾子 | 作用 |
|:--|:--|
|.left | マウスの左ボタンが押された時のみハンドラを呼び出す |
|.right | マウスの右ボタン… |
|.middle | マウスの中央ボタン… |


 キー修飾子

```html:html
<input v-on:keydown.13="handler">
<!-- よく使用されるキーコードはエイリアスとして登録されている -->
<input v-on:keydown.enter="handler">
```

使用可能なキーコードのエイリアス

| 修飾子 | 作用 |
|:--|:--|
|.enter | エンターキーが押された時 | 
|.tab | Tabキーが押された時 | 
|.delete | Deleteキーが押された時 | 
|.esc | ESCキーが押された時 | 
|.space | スペースキーが押された時 | 
|.up | 矢印の上キーが押された時 | 
|.down | 矢印の下キーが押された時 | 
|.left | 矢印の左キーが押された時 | 
|.right | 矢印の右キーが押された時 | 

複数つけた場合、ORを意味する

```html:html
<input v-on:keydown.up.down.left.right="handler">
```

システム修飾子

| 修飾子 | 作用 |
|:--|:--|
|.ctrl | Ctrlキーが押されている場合 |
|.alt |  Altキーが押されている場合 |
|.shift |  Shiftキーが押されている場合 |
|.meta |  Metaキーが押されている場合 |


## 2. フォーム入力バインディング（v-model）

双方向データバインディング（フォームの入力値および選択値をデータと同期させる）

インプット

```html:html
<input v-model="message">
<p>{{ message }}</p>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    message: 'Hello!'
  }
})
```

テキストエリア

```html:html
<textarea v-model="message"></textarea>
<pre>{{ message }}</pre>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    message: 'Hello!'
  }
})
```

チェックボックス（単一要素）

```html:html
<label>
  <input type="checkbox" v-model="val"> {{ val }}
</label>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    val: true//値の型はBoolean
  }
})
```

チェックボックス（複数要素）

```html:html
<label><input type="checkbox" v-model="val" value="A"> A</label>
<label><input type="checkbox" v-model="val" value="B"> B</label>
<label><input type="checkbox" v-model="val" value="C"> C</label>
<p>{{ val }}</p>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    val: []//値の型は配列 チェックされている値が格納される
  }
})
```

ラジオボタン

```html:html
<label><input type="radio" value="a" v-model="val"> A</label>
<label><input type="radio" value="b" v-model="val"> B</label>
<label><input type="radio" value="c" v-model="val"> C</label>
<p>{{ val }}</p>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    val: ''//値の型は文字列
  }
})
```

セレクトボックス（単一要素）

```html:html
<select v-model="val">
  <option disabled="disabled">選択してください</option>
  <option value="a">A</option>
  <option value="b">B</option>
  <option value="c">C</option>
</select>
<p>{{ val }}</p>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    val: ''//値の型は文字列
  }
})
```

セレクトボックス（複数要素）

```html:html
<select v-model="val" multiple>
  <option value="a">A</option>
  <option value="b">B</option>
  <option value="c">C</option>
</select>
<p>{{ val }}</p>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    val: []//値の型は配列
  }
})
```

画像ファイル

```html:html
<input type="file" v-on:change="handleChange">
<div v-if="preview"><img v-bind:src="preview"></div>
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    preview: ''
  },
  methods: {
    handleChange: function (event) {
      var file = event.target.files[0]
      if (file && file.type.match(/^image\/(png|jpeg)$/)) {
        this.preview = window.URL.createObjectURL(file)
      }
    }
  }
})
```

range

```html:html
<input type="range" v-model.number="val"> {{ val }}
```

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    val: 50
  }
})
```

v-modelの修飾子

| 修飾子 | 作用 |
|:--|:--|
| .lazy | inputの代わりにchangeイベントをハンドルする |
| .number | 値を数値に変換する |
| .trim | 値の余分なスペースを削除する |

## 3. マウント要素外（window,body）のイベントと操作

スクロールイベントの取得

```js:Vue.js
new Vue({
  el: '#app',
  data: {
    scrollY: 0,
    timer: null
  },
  created: function () {
    //ハンドラを登録
    window.addEventListener('scroll', this.handleScroll)
  },
  beforeDestroy: function () {
    //ハンドラを解除（コンポーネントやSPAの場合忘れずに！）
    window.removeEventListener('scroll', this.handleScroll)
  },
  methods: {
    //違和感のない程度に200ms間隔でscrollデータを更新する例
    handleScroll: function () {
      if (this.timer === null) {
        this.timer = setTimeout(function () {
          this.scrollY = window.scrollY
          clearTimeout(this.timer)
          this.timer = null
        }.bind(this), 200)
      }
    }
  }
})
```

ライブラリで実装

```html:html
<script src="https:cdn.jsdelivr.net/npm/smooth-scroll@12.1.5"></script>
<div id="app">
  <div class="content">...</div>
  <div v-on:click="scrollTop">
    ページ上部へ移動
  </div>
</div>
```

```js:Vue.js
var scroll = new SmoothScroll()
new Vue({
  el: '#app',
  methods: {
    scrollTop: function () {
      scroll.animateScroll(0)
    }
  }
})
```

Vue.js以外からのイベントの受け取り（dispatchEventを使用してイベントを検知）

```html:html
<div id="app">
  <input id="message" v-on:input="handleInput">
  <button data-update="jQuery!">jQueryからの更新</button>
</div>
```

```js:Vue.js
$(document).on('click', '[data-update]', function () {
  $('#message').val($(this).attr('data-update'))
  //入力値を更新したらイベントを発生させる
  $('#message')[0].dispatchEvent(new Event('input'))
})
new Vue({
  el: '#app',
  methods: {
    handleInput: function (event) {
      console.log(event.target.value)
    }
  }
})
```