イベント

##イベントドリブンプログラミング

あるイベントに対してどういった処理を行うのかを登録していくこと。登録する処理のことを、イベントハンドラもしくはイベントリスナという。イベント発生の流れは、ユーザーの操作に従って、webブラウザがイベントを発火、そして発火したイベントに対するイベントハンドラを実行

* **イベントハンドラ**・・・1つの要素、イベントについて１つしか設定できない
* **イベントリスナ**・・・1つの要素に複数イベントを設定することができる。


##イベントの登録には複数の方法がある

1. HTML要素の属性に指定
2. DOM要素のプロパティに指定
3. EventTarget.addEventListener()を利用。


#####1. HTML要素の属性に指定
```html:html
<input type="button" onclick="console.log('say')">
<input type="button" onclick="say()" >

<script>
  function  say() {
    console.log('say');
  }
</script>
```

#####2. DOM要素のプロパティに指定

```js:js 
const btn = document.getElementById('foo');

//sayを呼び出す関数
function say() {
  console.log('say');
}

//heyを呼び出す関数
function hey() {
  console.log('hey');
}

//イベントハンドラ　一つしか登録できない
btn.onclick = say;
btn.onclick = hey;
//hey

//ちなみに下の表記だと関数呼び出しで返り値を設定することになるので誤り
btn.onclick = say();
```

#####3. EventTarget.addEventListener()を利用。
*イベントの実行順は、登録された順番に発火することに留意。

```js:js
//イベントリスナ　複数の処理を登録できる。
btn.addEventListener('click', say, false);
btn.addEventListener('click', hey, false);
//say
//hey
```




##イベントハンドラの種類と、イベントリスナに登録できるイベントの種類

以下のもの以外にもイベントハンドラにはonがつく。他にも種類はある。

| イベントハンドラ | イベント  | 発火タイミング                                |
| ---------------- | --------- | --------------------------------------------- |
| onclick          | click     | マウスをクリック                              |
| ondbclick        | dblcick   | マウスをダブルクリック                        |
| onmousedown      | mousedown | マウスボタンを押下                            |
| onmouseup        | mouseup   | 押されたマウスボタンを離した                  |
| onkeydown        | keydown   | キーをお押した                                |
| onkeyup          | Keyup     | 押されたキーを離した                          |
| onchange         | change    | input要素の内容が変更された                   |
| onblur           | blur      | input要素のフォーカスが他に移った             |
| onfocus          | focus     | input要素のフォーカスがあたった               |
| onselect         | select    | input要素やtextarea要素でテキストが選択された |
| onsubmit         | submit    | フォームが送信された                          |
| onreset          | reset     | フォームがリセットされた                      |
| onload           | load      | 文書のロードが完了した                        |
| onabort          | abort     | 画像の読み込みが中断された                    |
| onerror          | error     | エラーが起きた                                |
| onresize         | resize    | ウィンドウサイズが変更された                  |



##イベントハンドラ、イベントリスナ内でのthisの挙動

thisが参照するオブジェクトは、イベントハンドラが設定した要素自身となる。

```js:js
const btn = document.getElementById('foo');

//thisはbtnを指す。
function say() {
  this.classList.add('class');
}

btn.addEventListener('click', say, false);
```

##イベントバブリング

イベントが発火した場合、そのイベントは伝搬する。
伝搬するフェーズを分けると3種類あり、

1. キャプチャリングフェーズ
2. ターゲットフェーズ
3. バブリングフェーズ

例えば、以下のようなHTMLがあった場合

```html:html
<!DOCTYPE html>
<html lang="en">
<body>
  <div id="outer">
    <div id="target" onclick ='console.log("hello")'>ターゲット</div>
  </div>
</body>
</html>
```
クライアントサイドJSでは、グローバルオブジェクトがwindowオブジェクトであり、ドキュメントプロパティを持つ。documetnオブジェクトはDOMツリーもルートノードであることから、最初にたどるのはこの2つ。

**キャプチャリングフェーズ()**
`window -> document -> html -> body -> outer　-> target`

**ターゲットフェーズ**
`target`で、イベントが発火する

**バブリングフェーズ(子要素から親要素をたどる)**
`target -> outer -> body -> html -> document -> window`



##実際のイベントの挙動
通常イベントはキャプチャリングフェーズで発生し、伝搬していく。
先程のHTMLをCSSで以下のようなボックスに設定し、それぞれにクリックイベントを設定する。
![スクリーンショット 2018-09-05 14.17.09.png](https://qiita-image-store.s3.amazonaws.com/0/179423/14a72ebb-f37c-b2e3-e78d-14a838216e7b.png)

* 赤い部分がouter
* 青い部分がtarget

```js:js
const outer = document.getElementById('outer');
const target = document.getElementById('target');

outer.onclick = function() {alert("outer")};
target .onclick = function() {alert("target")};
```

1. 赤をクリック -> アラート'outer'
2. 青をクリック -> アラート'target' -> アラート'outer'

バブリングフェーズでクリックイベントが伝搬し、targetで生じたクリックイベントが親要素のouterでも発生した。これがイベントの伝搬である。このときアウターをclickではなく別のイベントに変更すると、イベントの種類が違うので発生はしない。クリックイベントが伝搬していくと考える。


####キャプチャフェーズの変更
* イベントリスナの第三引数を変更する。
* trueだとキャプチャフェーズでイベントが発火、flaseだとバブリングフェーズでイベントが発火。初期値はfalse。
* イメージとして、先にイベントを発火させたい場合はtrueに、あとで発火させたい場合はfalseにする。

```js:js
const outerAlert = function(){
  alert('outer')
}

const targetAlert = function(){
  alert('target')
}

//どちらもtrueだと、キャプチャフェーズの順番で発火する
outer.addEventListener('click', outerAlert, true);
target.addEventListener('click', targetAlert, true);
//outer
//target

//outerをfalse。キャプチャリングでtarget、バブリングでouterが発火
outer.addEventListener('click', outerAlert, false);
target.addEventListener('click', targetAlert, true);
//target
//outer

//targetをfalse。キャプチャリングでouter、バブリングでtargetが発火
outer.addEventListener('click', outerAlert, true);
target.addEventListener('click', targetAlert, false);
//outer
//target
```


##preventDefault()とstopPropagation()

####0. 基本のバブリング

```js:js
const outerAlert = function(){
  alert('outer')
}

const targetAlert = function(){
  alert('target')
}

outer.addEventListener('click', outer, false);
target.addEventListener('click', target, false);
//target
//outer
```


####1. preventDefault()
ターゲットがリンクタグだと仮定すると、リンクの遷移をしない

```js:js
const outerAlert = function(){
  alert('outer')
}

const targetAlert = function(e){
  e.preventDefault();
  alert('target')
}

outer.addEventListener('click', outer, false);
target.addEventListener('click', target, false);
//target
//outer
//リンク遷移はしない
```

####2.stopPropagation()
イベントの伝播を止める。設定した物のイベントは発生する。

```js:js
const outerAlert = function(){
  alert('outer')
}

const targetAlert = function(){
  e.stopPropagation();
  alert('target')
}

outer.addEventListener('click', outer, false);
target.addEventListener('click', target, false);
//target
//イベントが伝搬せず、outerは発火しない。
```

####3. stopImeddiatePropagation() 
現在のリスナーターゲットに保存されている他の処理も止める。

```js:js
const outerAlert = function(){
  alert('outer')
}

const targetAlert = function(){
  alert('target')
}

const targetSay = function(){
  stopImmediatePropagation();
  alert('say')
}

outer.addEventListener('click', outer, false);
target.addEventListener('click', say, false);
target.addEventListener('click', target, false);
//outer
//targetに登録されているイベントリスナのイベントをストップ
```

####4. return false
読み込んだ以降の処理を止める。

```js:js
const outerAlert = function(){
  alert('outer')
}

const targetAlert = function(){
  return false;
  alert('target')
}

outer.addEventListener('click', outerAlert, false);
target.addEventListener('click', targetAlert, false);
//outer
//targetは処理が止まり表示されない。
```