jQueryのイベントハンドラ内でのpreventDefault()、stopPropagation()、stopImmediatePropagation()、return falseについて

jQueryのイベントオブジェクトが持つメソッドについて説明します。

# preventDefault()
preventDefaultはブラウザの持っている元々の処理を抑制します。

```html:HTML
<p><a href="#" class="jsc-trigger">cilck</a></p>
```

```js:JavaScript
$('.jsc-trigger').on('click', function(e) {
    e.preventDefault();
    alert(1);
});
```

上記の例でpreventDefault()を行わなければクリックした際に「#」の影響でページトップへスクロールしてしまいますが、preventDefault()を使用することでaタグのhrefを無効化し、ページトップへスクロールしてしまうことを防げます。

なお、すでにpreventDefault()が呼び出されているかどうかはisDefaultPrevented()を実行する事で確認が可能です。

# stopPropagation()
stopPropagationはイベントのバブリングを停止します。

https://jsfiddle.net/LjLrx4kb/

```html:HTML
<div class="jsc-wrapper">
  <p><a href="javascript:void(0);" class="jsc-trigger">click</a></p>
</div>
```

```js:JavaScript
$('.jsc-trigger').on('click', function(e) {
    e.stopPropagation(); // ここでstopPropagation()を実行すると…
    alert(1);
});

$('.jsc-wrapper').on('click', function() { // 親要素にバインドされているイベントが発火しなくなります。
    alert(2);
});
```

上記の例でstopPropagation()を実行しない場合はalert(1)、alert(2)が順番に実行されますが、stopPropagation()を実行した場合はバブリングが停止され、alert(2)は実行されなくなります。

なお、すでにstopPropagation()が呼び出されているかどうかはisPropagationStopped()を実行する事で確認が可能です。

# stopImmediatePropagation()
stopImmediatePropagationは同一要素に後からバインドされたイベントの発火を停止します。
また、合わせてバブリングも停止します（stopPropagation()を実行した場合と同等の処理が行われます）。

https://jsfiddle.net/0n8uj44t/

```html:HTML
<div class="jsc-wrapper">
  <p><a href="javascript:void(0);" class="jsc-trigger">click</a></p>
</div>
```

```js:JavaScript
var $trigger = $('.jsc-trigger');

$trigger.on('click', function(e) {
  e.stopImmediatePropagation();  // ここでstopImmediatePropagation()を実行することで…
  alert(1);
});

$trigger.on('click', function() {  // あとからバインドしたイベントが発火しなくなりつつ…
  alert(2);
});

$('.jsc-wrapper').on('click', function() {  // バブリングも停止される為、親要素にバインドされているイベントも発火しません。
  alert(3);
});
```

上記の例でstopImmediatePropagation()を実行しない場合はalert(1)、alert(2)、alert(3)が順番に実行されますが、stopImmediatePropagation()を実行した場合は後続のイベントの発火、バブリングが停止され、alert(2)、alert(3)は実行されなくなります。

なお、すでにstopImmediatePropagation()が呼び出されているかどうかはisImmediatePropagationStopped()を実行する事で確認が可能です。

# return false
イベントハンドラ内でreturn falseを実行した場合、以下の2つの処理を実行した事と同等の意味を持ちます。

・preventDefault()
・stopPropagation()

```html:HTML
<div class="jsc-wrapper">
  <p><a href="#" class="jsc-trigger">click</a></p>
</div>
```

```js:JavaScript&nbsp;その1
$('.jsc-trigger').on('click', function(e) {
    alert(1);
    return false;
});

$('.jsc-wrapper').on('click', function() {
    alert(2);
});
```

以下のコードはJavaScript その1のコードと同じ意味になります。
preventDefault()、stopPropagation()を実行する位置はどこで実行しても差はありませんが、個人的にはイベントハンドラの先頭で行ったほうが実行している事が分かりやすくて良いと思っています。

```js:JavsScript&nbsp;その2
$('.jsc-trigger').on('click', function(e) {
    e.preventDefault();
    e.stopPropagation();
    alert(1);
});

$('.jsc-wrapper').on('click', function() {
    alert(2);
});
```

### ただし、そもそもreturn falseは使うべきではない
前述の通りreturn falseを実行した場合はpreventDefault()とstopPropagation()が実行される為、それぞれの意味を理解した上で両方の処理を実行したい場合は使用しても実装上は問題ありません。ただ両方の処理が実行される事を理解せずに使用した場合にpreventDefault()が問題になるケースはあまりないかもしれませんが、stopPropagation()が実行されることでバブリングを利用したイベントのバインドをしている実装がある際に、その処理が実行されなくなってしまうという問題が発生してしまいます。

JSの実装者が完全に本人のみで、すべての実装仕様を理解している状態であれば問題ありませんが、複数人で実装を行っている場合に自分の知らないところでバグを引き起こす可能性がある為、基本的にはreturn falseは使用しない方が良いと考えます。

もしpreventDefault()とstopPropagation()の両方を実行したい場合でもreturn falseではなく、あえてpreventDefault()とstopPropagation()の両方を明記することで実装者の意図が伝わりやすくなるため、そういった意味でもreturn falseは使用しないほうが良いと考えます。
※とは言えもちろんstopPropagation()を記述して使ったとしてもバブリングを停止した事によるバグが発生する可能性は変わらず存在するので、実装仕様を把握した上で使う必要はあります。

また、ただ単純に処理を抜けたいという場合もあると思いますが、その場合はreturn falseではなく「return」のみを使用しましょう。returnのみであればpreventDefault()もstopPropagation()も実行されません。

# まとめ
それぞれのメソッドの処理の対応を以下に纏めます。

|            | ブラウザの<br />デフォルト動作抑制 | バブリング停止 |後続のイベント停止 |
|:----------:|:-----------:|:------------:|:------------:|
| preventDefault()       |        ◯ |     ×     |     ×     |
| stopPropagation()     |      × |    ◯    |    ×    |
| stopImmediatePropagation()       |        × |     ◯     |     ◯     |
| return false       |        ◯ |     ◯     |     ×     |

# おまけ
## jQueryではなく生JSでの実装について
ちょっとした豆知識です。

preventDefault()、stopPropagation()、stopImmediatePropagation()は恐らくほぼjQueryでしか使用する機会が無いと思うのですが、実は生のJavaScriptにも実装されているメソッドです。

ただし、古いIEでは実装されておらず、実装されていないIEでは同様の処理は行うことはできるものの異なる記述を行う必要があります。以下にpreventDefault()を例にご紹介します。

```js:JavaScript
// イベントハンドラ
var eventHandler = function(e){
    // preventDefaultが実装されていないIE8以前のブラウザ用
    var e = e || window.event;

    // preventDefaultが実装されているブラウザ用
    if (e.preventDefault) {
        e.preventDefault();
    }
    
    // preventDefaultが実装されていないIE8以前のブラウザ用
    if (e.returnValue) {
        e.returnValue = false;
    }
};
```

stopPropagation()、stopImmediatePropagation()の場合も対応しているかどうかで別途異なる実装が必要となります。このように対応しているかどうかを確認しながらそれぞれ実装するのは手間ですし、バグの元にもなりかねないのでjQueryで使用するのが良いでしょう。


# 参考
http://stackoverflow.com/questions/5302903/jquery-event-stopimmediatepropagation-vs-return-false