LoginSignup
4

More than 5 years have passed since last update.

イベント

Posted at

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

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

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

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

  1. HTML要素の属性に指定
  2. DOM要素のプロパティに指定
  3. EventTarget.addEventListener()を利用。
1. HTML要素の属性に指定
html
<input type="button" onclick="console.log('say')">
<input type="button" onclick="say()" >

<script>
  function  say() {
    console.log('say');
  }
</script>
2. DOM要素のプロパティに指定
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
//イベントリスナ 複数の処理を登録できる。
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
const btn = document.getElementById('foo');

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

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

イベントバブリング

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

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

例えば、以下のような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

  • 赤い部分がouter
  • 青い部分がtarget
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
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
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
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
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
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
const outerAlert = function(){
  alert('outer')
}

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

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4