15 Event - iruma-tea/dokushujs GitHub Wiki
JavaScriptでは、何らかの契機でブラウザから発生した通知をイベントとして受け取る。
イベントには、クリック操作やスクロール操作、フォームへの入力などのユーザー起因のイベントや画面ロードの完了などの
ブラウザ起因のイベントがある。
JavaScriptでイベントの発生を検知し、何らかの処理を実行するには、イベントが発生したときに実行される関数(アクションと呼びます)をイベントハンドラまはた、イベントリスナに登録する。
JavaScriptでイベントを登録する2つの方法
- イベントハンドラにアクションを登録する。
- イベントリスナにアクションを登録する。
イベントハンドラとは、イベントが発生したときに実行される関数(アクション)のこと。
Windowオブジェクト(window)、Elementオブジェクト、Documentオブジェクト(document)などのonから始まるプロパティに対して、
アクションを設定することで、イベント発生時に特定の処理を実行できる。
[構文]イベントハンドラの登録方法
EventTarget.on{イベントタイプ}=action;
EventTarget: この要素でイベントが発生したときに、登録された関数(action)が実行される。Elementオブジェクト、window、documentなどが使用可能。
on{イベントタイプ}: イベントタイプにはclickなどが入る。例えば、 クリックイベントの場合はonclickになる。
action: イベント発生時に実行したい関数を登録する。名前付き関数、または無名関数、アロー関数を登録できる。
また、actionの第一引数はEventオブジェクトが渡される。
・ クリック操作を検知するイベントハンドラの登録
<div>
<button id="minus">-</button>
<span id="number">0</span>
<button id="plus">+</button>
<script>
let count = 0;
// Elementの取得
const number = document.querySelector("#number");
const plusBtn = document.querySelector("#plus");
const minusBtn = document.querySelector("#minus");
plusBtn.onclick = function(event) {
count++;
number.textContent = count;
};
minusBtn.onclick = function(event) {
count--;
number.textContent = count;
};
</script>
</div>
イベントハンドラをnullで初期化することによって、登録したアクションを解除できる。
[構文]イベントハンドラの解除方法
EventTarget.on{イベントタイプ} = null;
・ ボタンに登録したアクションを解除
<button>アラート</botton>
<script>
const btn = document.querySelector("botton");
btn.onclick = function() {
alert("アラート!");
}
btn.onclick = null;
</script>
イベントリスナとはイベントにアクションを紐づける仕組みのこと
イベントが発火するとイベントに登録されたアクションが実行される点はイベントハンドラと同じであるが、
次の3つの点が異なる。
- オプション指定が可能
- アクションが実行される条件をオプションで指定できる。
- 複数のアクション(関数)の登録が可能
- 複数のアクションを個別に登録できる。
- アクション毎に登録解除が可能
- 複数のアクションを個別に解除できる。
イベントリスナを登録するには、addEventListenerメソッドを使う。
[構文] イベントリスナとアクション(関数)を登録
EventTarget.addEventListener("イベントタイプ",action[,options]);
EventTarget: この要素でイベントが発生するときに、登録された関数(action)が実行される。Elementオブジェクトやwindow、documentなどが使用可能です。
"イベントタイプ": イベントタイプを文字列を入力する。onは先頭につかない。例えば、クリックイベントの場合、clickとなる。
action: イベント発生時に実行したい関数を登録する。名前付き関数、無名関数、アロー関数を登録できる。また、actionの第一引数には、Eventオブジェクトが渡される。
options: optionsではイベントリスナの挙動の設定を行うことができる。真偽値またはオブジェクトを設定可能。(※)
(※) optionsのプロパティ
プロパティ | 説明 |
---|---|
capture | 登録されたアクションをキャプチャリングフェーズで実行する。デフォルトはfalse。 |
once | アクションの実行を一度きりとする場合にtrueを渡す。実行後は自動的にアクションは削除される。デフォルトはfalse。 |
passive | trueとした場合には、パッシブリスナが有効になる。パッシブリスナが有効な状態では、アクション内で呼び出されるEvent.preventDefaultはブラウザのデフォルト処理を停止しない。 |
<div>
<button id="minus">-</button>
<span id="number">0</span>
<button id="plus">+</button>
</div>
<script>
let count = 0;
// Elementの取得
const number = document.querySelector("#number");
const plusBtn = document.querySelector("#plus");
const minusBtn = document.querySelector("#minus");
plusBtn.addEventListener("click", function(event) {
count++;
number.textContent = count;
});
minusBtn.addEventListener("click", function(event) {
count--;
number.textContent = count;
});
</script>
イベントリスナを使うと、複数のアクションをイベントに紐づけることができる。
なお、イベントハンドラに直接登録するときは関数を一つのしか登録できない。
・ 複数のアクションをイベントリスナとして登録
<div>この要素の色が変わります。</div>
<button id="color">文字色の変更</button>
<button id="bg-color">背景色の変更</button>
<button id="all-color">文字色と背景色の変更</button>
<button id="reset-color">リセット</button>
<script>
const targetEl = document.querySelector("div");
const colorBtn = document.querySelector("#color");
const bgColorBtn = document.querySelector("#bg-color");
const allColorBtn = document.querySelector("#all-color");
const resetBtn = document.querySelector("#reset-color");
function colorChange(event) {
targetEl.style.color = "#ff0000";
}
function bgColorChange(event) {
targetEl.style.backgroundColor = "blue";
}
function reset(event) {
targetE1.style.backgroundColor = "";
targetE1.style.color = "";
}
colorBtn.addEventListener("click", colorChange);
bgColorBtn.addEventListener("click", bgColorChange);
allColorBtn.addEventListener("click", colorChange);
allColorBtn.addEventListener("click", bgColorChange);
resetBtn.addEventListener("click", reset);
</script>
removeEventListenerメソッドを使用することで、登録したアクションの解除を行う。
[構文]removeEventListenerの記法
EventTarget.removeEventListener("イベントタイプ", action);
EventTarget : addEventListenerでイベントリスナの登録時に渡した要素と同じ要素を指定する。
action: addEventListenerでイベントリスナの登録時に渡したのと同じ関数への参照を渡すことで、その関数をイベントリスナの対象から除外する。
・ アクション解除の例
<div>色は変わりません。</div>
<button id="color">文字色の変更</button>
<script>
const targetEl = document.querySelector("div");
const colorBtn = document.querySelector("#color");
function colorChange(evnet) {
targetEl.style.color = "#ff0000";
}
colorBtn.addEventListener("click", colorChange);
colorBtn.removeEventListener("click", colorChange);
</script>
<button onclick="alert('ボタンがクリックされました。')">ボタン</button>
ただし、この方法ではHTMLファイル内にJavaScripコードを記述するため、プロジェクトが大きくなってくると、
コードを整理するのが困難になってくるため、基本的にはイベントハンドラやイベントリスナに対してアクションを追加する。
ページ内のどこかの要素でイベントが発生した場合にはイベントが要素間を伝播して行きます。これをイベントの伝播と呼びます。
イベントの伝播は、キャプチャリングフェーズ、ターゲットフェーズ、バブリングフェーズの3つに分けることができる。
HTML内の要素でイベントが発生した場合には、これら3つのフェーズが順番に実行される。
イベントの伝番で最初に発生するのフェーズ。キャプチャリングフェーズでは、発生したイベントと同じタイプのイベント(例えば、クリックイベント)が上位から下位へ伝播していく。
具体的にはHTML内の何れかの要素でイベントが発生した場合には、最上位のWindowオブジェクトからイベントの伝播が始まり、Documentオブジェクト、要素の順でイベントが発生した要素まで同じイベントが順番に伝わっていく。
なお、イベントハンドラやイベントリスナで登録したアクションは、キャプチャリングフェーズでは 一般的には実行されない。
イベントリスナのオプションで{capture: true}を設定したアクションのみ、このフェーズが実行される。
// キャプチャリングの確認
<!DOCTYPE html>
<html>
<body>
<div id="div1"></div>
<div id="div2">
<button>ボタン</button>
</div>
<script>
const div1 = document.querySelector("#div1");
const div2 = document.querySelector("#div2");
const button = document.querySelector("#button");
// windowオブジェクトにclickイベントを登録
window.addEventListener("click", () => {
console.log("windowのclickイベント");
},{capture: true});
// div1にクリックイベントを登録
div1.addEventListener("click", () => {
console.log("div1のclickイベント");
}, {capture: true});
// div2にクリックイベントを登録
div2.addEventListener("click", () => {
console.log("div2のclickイベント");
}, {capture: true});
// buttonにclickイベントを登録
button.addEventListener("click", () => {
console.log("buttonのclickイベント");
}, {capture: true});
</script>
</body>
</html>
・ボタンクリック後のログ
> windowのclickイベント
> div2のclickイベント
> buttonのclickイベント
キャプチャリングフェーズによって、イベントの伝播がイベントが発生した要素までたどり着くと、
ターゲットフェーズになる。このフェーズではイベントが発生した要素に登録しているアクションを実行する。
ターゲットフェーズが完了すると、今度はイベントが上位の親要素、祖先要素に対してイベントの伝播が起こります。
これをバブリングフェーズと呼ぶ
バブリングフェーズでは、親要素、祖先要素、doucment、windowで同じイベントタイプに登録されているアクションが実行される。
また、イベントリスナに{capture:true}が設定されているアクションは、キャプチャリングフェーズで、すでに実行済みのため実行されない。
イベントリスナに{capture:true}をつけていない場合には、バブリングによってイベント伝播によるアクションの実行を行う。
・ バブリングの確認
<!DOCTYPE html>
<html>
<body>
<div id="div1"></div>
<div id="div2">
<button>ボタン</button>
</div>
<script>
// Element オブジェクトの取得
const div1 = document.querySelector("#div1");
const div2 = document.querySelector("#div2");
const button = document.querySelector("button");
// windowにclickイベントを登録(caputer:trueとして登録)
window.addEventListener("click", () => {
console.log("windowのclickイベント");
}, {capture: true});
// div2にclickイベントを登録
div2.addEventListener("click", () => {
console.log("div2のclickイベント");
});
// buttonにclickイベント
button.addEventListener("click", () => {
console.log("buttonのclickイベント");
});
</script>
</body>
</html>
ボタンクリック後のログ
> windowのclickイベント
> buttonのclickイベント
> div2のclickイベント
一部のイベントタイプ(例えば、mouseleaveやfocus)では、バブリングは発生しない。
また、バブリングが発生するかどうかはEventオブジェクトのbubblesプロパティがtrueかどうかで判定できる。
イベントハンドラやイベントリスナに登録した関数の引数に渡されるEventオブジェクトについて記載する。
[構文]Eventオブジェクト
EventTarget.on{イベントタイプ} = function(event) {...}
EventTarget.addEventListener("イベントタイプ", function(event) {...});
event: Eventオブジェクト。このEventオブジェクトは、Eventコンストラクタを継承した別のコンストラクタから
インスタンス化されたオブジェクトです。
例えば、inputイベントなどに登録したアクションには、Eventコンストラクタを継承したInputEventコンストラクタから
生成されたオブジェクトが渡される。そのため、イベントタイプによってアクセス可能なプロパティが変わる。
Eventオブジェクトには、イベントの発生状況に関わる情報が格納されている。
Eventオブジェクトに設定されるプロパティはイベントタイプによって変わるが、以下はどのイベントタイプでも
使用できる共通のプロパティとメソッド。
メソッド | 説明 |
---|---|
preventDefault() | ブラウザのデフォルト処理の実行を抑止する抑止可能なイベントタイプはcancelableがtrueのイベント |
stopPropagation() | キャプチャリング、バブリングによるイベント伝播を抑止する。 |
stopImmediatePropagation() | stopPropagation()の作用に加え、自要素に対して複数のアクションが登録されている場合は、後続のアクションの実行を抑止する。 |
プロパティ | 説明 |
---|---|
type | イベントタイプが文字列で渡される(例:click) |
cancelable | preventDefault()を使って、デフォルト処理をキャンセル可能かどうかを返す(true/false)。trueの場合、キャンセル可能 |
bubbles | 発生中のイベントでバブリングが発生するかどうかを返す。trueの場合、バブリングが発生する。 |
currentTarget | アクションを登録した(ターゲット)要素を返す。キャプチャリングやバブリングが発生しているときも、常にアクションを登録した要素を返すことに注意。 |
target | **実際にイベントが発生した要素を返す。**キャプチャリング、バブリングの作用によって実際にイベントが発生した要素とアクションが登録されている要素が異なる可能性に注意。 |
defaultPrevented | preventDefault()によって、ブラウザのデフォルト処理がキャンセルされたかどうかを表す。(true/false) trueの場合、キャンセルされた状態を表す。 |
eventPhase | 実行中のイベントフェーズを表す(※) |
timestamp | ドキュメントの生成からイベントの発生までの時間を返す。 |
isTrusted | イベントがユーザー操作によって発生したものか、スクリプトによって発生したものかを判定する。(true/false)。ユーザー操作によって発生した場合、true |
(※) 実行中のイベントフェーズを表す。
数値(定数) | 説明 |
---|---|
0 (Event.NONE) | 実行中のイベントがない状態 |
1 (Event.CAPTURING_PHASE | キャプチャリングフェーズ |
2 (Event.AT_TARGET) | ターゲットフェーズ |
3 (Event.BUBBLING_PHASE | バブリングフェーズ |
stopPropagationメソッドを呼び出すことで、他の要素への伝播を止めます。
キャプチャリングフェーズで呼び出された場合、下位の要素に設定されているアクションが実行されることはない。
ターゲットフェーズ、バブリングフェーズで呼び出された場合には、上位の要素にイベント伝播しない。
・ キャプチャリングフェーズでイベント伝播を停止した場合
<section>
section <br />
<div>
div <br />
<button>button</button>
<div>
<p>p<br />この要素は伝播の対象外です。</p>
<section>
<style>
section, div, p, button {
padding: 10px;
border: 10px solid skyblue;
}
</style>
<script>
const button = document.querySelector("button");
const div = document.querySelector("div");
conse section = document.querySelector("section");
// buttonに対するアクションを登録
button.addEventListener("click", (event) => {
console.log("buttonのclickイベントが実行されました。");
});
// divに対するアクションを登録
div.addEventListener("click", (event) => {
console.log("divのclickイベントが実行されました。");
});
// sectionに対するアクションを登録
section.addEventListener("click", (event) => {
event.stopPropagation();
console.log("sectionのclickイベントが実行されました。");
},{capture: true}); // キャプチャリングフェーズでアクションを実行
</script>
ボタンをクリック後のログ
> sectionのclickイベントが実行されました。
div buttonのアクションは実行されない!
同じ要素に複数のアクションが登録されていた場合、後続のアクションへの伝播も止めたいときには、stopImmediatePropagationを使う。
stopImmediatePropagationでは実行中のアクションが完了するとイベント伝播は即時で終了する。
Event.stopImmediatePropagationの使用例
<div>この要素の色が変わります。</div>
<button id="all-color">文字色と背景色の変更</button>
<script>
const targetEl = document.querySelector("div");
const allColorBtn = document.querySelector(""all-color);
// 文字色を変更するアクション
function colorChange(event) {
event.stopImmediatePropagation(); // 後続のアクションへのイベント伝播を停止
targetEl.style.color = "#ff0000";
}
// 背景色を変更するアクション
function bgColorChange(event) {
targetEl.style.backgroundColor = "blue";
}
// 関数を2つ登録
// アクションは登録された順番(colorChage → bgColorChange)で実行される。
allColorBtn.addEventListener("click", colorChange);
allColorBtn.addEventListener("click", bgColorChange);
</script>
HTMLタグによっては、ブラウザの特定の処理が実装されている場合がある。
例えば、アンカータグ(a)はリンクを作成するときに使いますが、このタグがクリックされた場合には、
ブラウザのデフォルト処理によって、リンク先URLに画面遷移する。
これらのブラウザのデフォルト処理を止めるには、preventDefaultをアクション内で実行する。
・ すべてのリンクの機能を無効化する
<a href="https://www.google.com">Googleへは行かせません。</a>
<a href="https://www.yaoo.co.jp">Yahooへは行かせません。</a>
<script>
// すべてのアンカータグ(a)を取得する
const links = document.querySelector("a");
// すべてのアンカータグに対してリンク無効化のアクションを追加
links.forEach(link => link.addEventListener("click", evnet => {
event.preventDefault();
alert(event.currentTarget.textContent); // アラートの表示
}))
</script>
EventオブジェクトのtargetとcurrentTargetには異なる要素が格納される場合があるため、注意が必要。
これはバブリングの作用によって起こる。
<body>
<section id="container">
<button>ボタン</button>
</section>
<script>
// #container(sectionタグにアクションを登録する)
const containerEl = document.querySelector("#container");
containerEl.addEventListener("click", function(event) {
console.log(`currentTarget: [ ${event.currentTarget.nodeName} ]`);
console.log(`target: [ ${event.target.nodeName} ]`);
});
</script>
</body>
ボタンクリック後
> currentTarget:[SECTION]
> target:[BUTTON]
buttonをクリックするとbuttonタグでclickイベントが発生する。このとき、target、currentTargetでは異なる要素が取得される。
currentTargetの場合は常に実行中のアクションが登録された要素が取得される。targetの場合イベントが発生した要素が取得される。
バブリングによってイベントが発生した要素と実行中のアクションが登録された要素が異なる場合には、それぞれ取得される要素が異なるため、注意が必要。
パッシブリスナやイベントをスクリプトから実行する方法を紹介する。
**パッシブリスナ(Passive Listener)**とはChrome51より導入されたスクロールのパフォーマンスを改善する仕組みです。
addEventListenerのオプションに{passive:true}を渡すことで、パッシブリスナが有効になる。
これにより、アクション内でのEvent.preventDefault()の実行を無効化します。
なお、Chromeの場合はデフォルトが{passive:true}です。
・ スクロール処理を禁止する
<body>
<div></div>
<style>
body {
margin: 0px;
}
div {
background: liner-gradient(#e66456, #9198e5);
height: 200vh;
}
</style>
<script>
function preventScroll(event) {
event.preventDefault();
}
document.addEventListener("touchmove", preventScroll, {passive: false});
document.addEventListener("wheel", preventScroll, {passive: false});
</script>
</body>
ユーザーの画面入力によって発生するclickイベントやinputイベントなどをJavaScriptのコードから発火する方法を記載する。
コードからイベントを発火するには、dispatchEventメソッド使用する
[構文] dispatchEventの記法
let cancelled = EventTarget.dispatchEvent(event);
EventTarget: イベントを発生させたい要素を設定する。Elementやwindow,documentなど。
event: 発火したいイベントタイプのEventオブジェクトを渡す。Eventオブジェクトは、new Event("イベントタイプ"[, {bubbles: true}])の
形式でインスタンス化する。(第二引数{bubbles: true}を省略した場合、バブリングは発生しない。)
cancelled : イベントで発火により実行されたアクションの中でEvent.preventDefaultが1回でも実行された場合にfalseが返る。それ以外はtrueを返す。
clickイベントをコードから発火
<span>0</span>
<button disabled>+</button>
<script>
const button = document.querySelector("button");
const span = document.querySelector("span");
let count = 0;
button.addEvenListener("click", fuction(event) {
count++;
span.textContent = count;
});
// Eventオブジェクトのインスタンス化
const myEvent = new Event("click");
// 1秒間隔でclickイベントを発火
setInterval(() => {
button.dispatchEvent(myEvent); // イベントを発火
}, 1000);
</script>
- キーボード操作に関するイベント
イベントタイプ 発火タイミング input <input>要素のvalue属性や<textarea>のコンテンツが変化するたびに発火する。 change <input>要素のvalue属性の値や<teaxtarea>のコンテンツ値が確定したタイミングで発火する。 keydown 任意のキーが押し込まれたときに発火する。 keypress
非推奨変わりにkeydownイベントを使用する。 keyup 任意のキーを離すタイミングで発火する compositionstart IMEによる編集セッションが開始したタイミングで発火する。 compositionend IMEによる編集セッションが終了したタイミングで発火する。 compositionupdate IMEによる編集セッション中で文字が追加されたときに発火する。 submit フォームが送信されたときに発火する - マウス操作に関するイベント
イベントタイプ 発火タイミング click マウスの左クリックが押されたときに発火する。 dbclick マウスの左クリックが2回連続されたときに発火する。 contextmenu コンテキストメニューを開くときに発火する。 mousedown マウスでクリックされたときに発火する。左クリック、右クリックどちらでも発火する。 mouseup マウスのクリックが離されるときに発火する。左クリック、右クリックどちらでも発火する。 mouseenter カーソルが要素内に入るタイミングで発火する。バブリングは発生しない mouseover カーソル要素、またはその子要素を通過するときに発火する。バブリングは発生しない。 mousemove カーソルが要素内で移動している間に連続的に発火する。バブリングは発生しない。 mouseleave カーソルが要素から出たときに発火する。バブリングは発生しない。 mouseout カーソルが要素から出たときに発火する。バブリングは発生しない。 - カット、コピー、ペーストなどに関わるイベント
イベントタイプ 発火タイミング copy コピー操作を行ったときに発火する。 paste ペースト操作を行ったときに発火する。 cut カット操作を行ったときに発火する。 - フォーカスに関するイベント
イベントタイプ 発火タイミング focusin 要素にフォーカスが当たったときに発火する。バブリングは発生する。 focus 要素にフォーカスが当たったときに発火する。バブリングは発生しない。 focusout 要素からフォーカスが外れたときに発火する。 バブリングは発生する。 blur 要素からフォーカスが外れたときに発火する。 バブリングは発生しない。 - スクロールや画面タッチに関わるイベント
イベントタイプ 発火タイミング scroll 画面がスクロースしている間は連続的に発火する。 wheel マウスのホイールが回転している間は連続的に発火する。 mousewheel
非推奨代わりにwheelイベントを使用する。 touchstart スマホのタッチ入力が開始されたタイミングで発火する。 touchmove スマホのタッチ操作が継続している間は連続的に発火する。 touchend スマホのタッチ入力が終了したタイミングで発火する。 - アニメーションに関わるイベント
イベントタイプ 発火タイミング transitionstart CSSのtransitionプロパティによるアニメーション開始時に発火する。 トランジションの遅延(transition-deley)を待ってから発火する。 transitionrun CSSのtransitionプロパティによるアニメーション開始時に発火する。 トランジションの遅延(transition-deley)を待たずに発火する。 transitionend CSSのtransitionプロパティによるアニメーション終了時に発火する。 animationstart CSSのanimationプロパティによるアニメーション開始時に発火する。アニメーションの遅延(animation-deley)を待ってから発火する。 animationend CSSのanimationプロパティのよるアニメーション終了時に発火する。 animationiteration CSSのanimationプロパティによるアニメーションが1回ループしたタイミングで発火する。 - 画面(window)に関わるイベント
イベントタイプ 発火タイミング load CSS(.css)/JS(.js)ファイル、画像などのHTMLから読み込んでいるすべてのリソースが読み込み完了した時点で発火する。 DOMContentLoaded DOMツリーの構築が完了した時点で発火する。DOM操作が行える状態になったことを表す最初のイベント。
CSS(.css)/JS(.js)ファイル、画像などの読み込みは完了していない可能性がある。DOCUMENTオブジェクトに対するイベントリスナとして登録可能readystatechange Documentオブジェクトに登録可能なイベントタイプ。画面の読み込みフェーズが変わったタイミングで発火する。
readyStateプロパティによって、現在どのフェーズであるか判断できる(※)beforeunload 現在表示中のページがアンロードされる直前に発火する。 unload 現在表示中のページがアンロードされたときに発火する。 resize 画面サイズが変更されたときい発火する。 online ブラウザがネットワーク接続の検知したタイミングで発火する。 offline ブラウザがネットワーク接続の切断を検知したタイミングで発火する。
(※) document.readyStateプロパティの値
値 | 説明 |
---|---|
loading | 画面ロード中、DOMツリーの構築も完了していない |
interactive | DOMツリーの構築が完了している。(DOMContentLoadedと同じ状態) |
complate | loadイベントの直前に発火する。HTMLおよびHTMLで読み込んでいるリソース(CSS/JSファイル、画像など)の読み込みも完了している。 |