イベントハンドラを登録するコードの記述位置に関する注意と対処方法
【開発環境】
OS:Win11(64ビット)
VSCode1.72.2、
クロム
【コード順序に依るエラー】
例文
<script>要素と<input>要素の順序を逆さにすると、エラー
>> TypeError: Cannot read property 'addEventListener' of null
理由は、
HTML ページはファイルのダウンロードが終わったあと、 HTML ページに記述されている内容をページの先頭から順にパース(解析)して DOM ツリーを構築し始め、<script>タグを見つける度、パースを中断してjavascriptのコードを実行し始める。
最初に getElementById メソッドを使って id 属性が 'xxx' の要素ノードを取得しようとしますが、この要素は <script> よりも後に記述されているため、まだパースが行われておらず見つけることができません。その結果、変数 button には null が格納されます。
次の文で addEventListener メソッドを実行しようとしますが、ターゲットである変数 button には null が格納されているため、エラーが発生する。
これは外部のファイルに JavaScript のコードを記述し、そのファイルを読み込んでいる場合でも同じである。
----対策方法---
【JavaScriptのコードはHTMLページの末尾に記述する】-----------
一つ目の方法はこれまでのサンプルでも行ってきたようにターゲットとなる要素を HTML ページで先に記述し、それよりもあとで <script> を記述することです。
サンプル
JS
動作結果--ボタンをクリックすると、アラームが表示する
【DOMContentLoadedイベントを利用する】--------------
ターゲットなる要素よりも前に <script> タグを記述したい場合はContentLoadedイベントを利用する。つまり、<head> タグ内に <script>タグを記述することが出来る。
これは、 JavaScript のコードが先に書いたとしても、HTMLの要素の解析が終わって DOM ツリーが完成すると、document.DOMContentLoaded というイベントが発生します。この時、addEventListenerイベントリスナーは JavaScriptのコードで書かれたイベントハンドラの実行をDOM ツリーが完成するまで、停止させるからである。
DOMContentLoaded イベントが発生すると、イベントリスナーとして登録したコードが実行され、ターゲットの要素の取得と click イベントへのイベントリスナーの登録が実行されます。
サンプルコード
「DOMContentLoaded.html」ファイルを書く
ブラウザを立ち上げ、ボタンをクリックする
【loadイベントを利用する】-------
document.ondomcontentloaded = function(event) {
console.log('DOM が完全に読み込まれました');
// ここに DOM の操作を記述
};
そこで、代わりに window オブジェクトで発生する load イベントを利用することにする。
つまり、DOM ツリーの構築が完了すると DOMContentLoaded イベントが発生しますが、そのあとで画像やスタイルシートなどすべてのリソースの読み込みが完了すると load イベントが発生します。これを利用して、ヘッダー部分に JavaScript のコードを書いてみる。
load イベントが発生すると、イベントハンドラとして登録したコードが実行され、ターゲットの要素の取得と click イベントへのイベントハンドラの登録が実行されます。
サンプル
「onload.html」ファイルを書く
外部のjsファイルを書く
ブラウザを立ち上げ、ボタンをクリックする
【scriptタグでdefer属性を設定する】------
この方法は JavaScript のコードを記述した外部のファイルを読み込む場合にだけ利用出来る。
defer 属性を設定された script タグで読み込まれた外部の JavaScript のファイルに記述されたコードは、すぐに実行されるのではなく DOM ツリーの構築が完了し DOMContentLoaded イベントが発生する直前に実行されます。
サンプル
jsファイル
ブラウザを立ち上げて、ボタンをクリックする
※コメント投稿者のブログIDはブログ作成者のみに通知されます