JavaScriptで要素にイベントを設置していると、同じ要素に対して同じ種類のイベントを複数設定したい時があります。
その際の実行順序のルールや制御方法についてまとめてみました。
同じイベントの実行順序はイベントの追加順序に依存する
例えば、ボタン要素に複数のクリックイベントを設定してみましょう。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sample Title</title>
</head>
<body>
<button id="button">ここをクリック</button>
<div id="div" class="js-keyup js-click"></div>
</body>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
// ボタン要素を取得
const button = document.getElementById("button");
// js-click-1のclickイベントを設置
const jsClick1Handler = function () {
console.log("js-click-1 clicked");
};
// js-click-2のclickイベントを設置
const jsClick2Handler = function () {
console.log("js-click-2 clicked");
};
// イベントリスナーの登録
button.addEventListener("click", jsClick1Handler);
button.addEventListener("click", jsClick2Handler);
});
</script>
</html>
イベントリスナーの登録順序を入れ替えると、実行順序も変わります。
// イベントリスナーの登録
button.addEventListener('click', jsClick2Handler);
button.addEventListener('click', jsClick1Handler);
つまり、イベントリスナーは登録順序に従って実行されることがわかります。
イベントの追加順序に依存することの問題点
イベントの追加順序に処理の順序が依存するため、常に追加順序を意識してコードを書く必要があります。
しかし、実際にイベントの追加順序を追うことは思ったよりも大変です。
例えば、先ほどのJavaScriptコードの一部が別ファイルに切り出されていたとしましょう。
document.addEventListener("DOMContentLoaded", function () {
// ボタン要素を取得
const button = document.getElementById("button");
// js-click-2のclickイベントを設置
const jsClick2Handler = function () {
console.log("js-click-2 clicked");
};
button.addEventListener("click", jsClick2Handler);
});
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sample Title</title>
</head>
<body>
<button id="button">ここをクリック</button>
<div id="div" class="js-keyup js-click"></div>
</body>
<script src="./index.js"></script>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
// ボタン要素を取得
const button = document.getElementById("button");
// js-click-1のclickイベントを設置
const jsClick1Handler = function () {
console.log("js-click-1 clicked");
};
// イベントリスナーの登録
button.addEventListener("click", jsClick1Handler);
});
</script>
</html>
一部のJavaScriptが切り出されると、index.html
を見ただけでは実行順序を追うことが難しくなります。
上記のコードでは、まずscript
タグで切り出されているjsClick2Handler
のイベントを登録します。
<script src="./index.js"></script>
その後、jsClick1Handler
のイベントが登録されます。
// ボタン要素を取得
const button = document.getElementById("button");
// js-click-1のclickイベントを設置
const jsClick1Handler = function () {
console.log("js-click-1 clicked");
};
// イベントリスナーの登録
button.addEventListener("click", jsClick1Handler);
index.js
の読み込み位置を変更すると、実行順序も変わります。
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
// ボタン要素を取得
const button = document.getElementById("button");
// js-click-1のclickイベントを設置
const jsClick1Handler = function () {
console.log("js-click-1 clicked");
};
// イベントリスナーの登録
button.addEventListener("click", jsClick1Handler);
});
</script>
<script src="./index.js"></script> <!-- ここに変更 -->
解決案:イベントの実行順序を制御する
イベントの登録順序に依存せず実行順序を制御する案として、片方のイベント内でもう片方のイベントを呼ぶ方法があります。
document.addEventListener('DOMContentLoaded', function() {
// ボタン要素を取得
const button = document.getElementById('button');
// js-click-1のclickイベントを設置
const jsClick1Handler = function(event) {
jsClick2Handler.call(this, event);
console.log('js-click-1 clicked');
};
// js-click-2のclickイベントを設置
const jsClick2Handler = function(event) {
console.log('js-click-2 clicked');
};
// イベントリスナーの登録
button.addEventListener('click', jsClick1Handler);
});
上記コードでは、jsClick1Handler
内の最初の処理でjsClick2Handler
を呼び出しています。また、イベントリスナーに登録しているのはjsClick1Handler
のみです。
このようにイベントリスナーを複数登録せずに、片方のイベント処理で別のイベントの実行を制御すると、実行順序をコントロールできます。
JavaScriptを切り出した時の例
JavaScriptの一部が切り出されている場合でも、同様に実行順序をコントロールできます。
下の例では、切り出されたjsClick2Handler
をimportで読み込み使用しています。
<script type="module">
// jsClick2Handlerの読み込み
import { jsClick2Handler } from './index.js';
document.addEventListener('DOMContentLoaded', function() {
// ボタン要素を取得
const button = document.getElementById('button');
// js-click-1のclickイベントを設置
const jsClick1Handler = function(event) {
jsClick2Handler.call(this, event);
console.log('js-click-1 clicked');
};
// イベントリスナーの登録
button.addEventListener('click', jsClick1Handler);
});
</script>
// ボタン要素を取得
const button = document.getElementById("button");
// js-click-2のclickイベントを設置
export const jsClick2Handler = function () {
console.log("js-click-2 clicked");
};
まとめ
同じ要素に同じ種類のイベントを複数設置する場合、実行順序はイベントリスナーへの登録順序に依存します。
この対策として、イベントリスナーへの登録は一つの関数内にまとめ、イベントの実行順序を制御する方法があります。