イベントハンドラを登録するコードの記述位置に関する注意と対処方法
【開発環境】
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」ファイルを書く
![](https://blogimg.goo.ne.jp/user_image/2e/93/602956da4e00df7b0150667214f706a5.jpg)
ブラウザを立ち上げ、ボタンをクリックする
![](https://blogimg.goo.ne.jp/user_image/4f/dc/47aa4c0a4322da99d26f95095486c525.jpg)
【loadイベントを利用する】-------
document.ondomcontentloaded = function(event) {
console.log('DOM が完全に読み込まれました');
// ここに DOM の操作を記述
};
そこで、代わりに window オブジェクトで発生する load イベントを利用することにする。
つまり、DOM ツリーの構築が完了すると DOMContentLoaded イベントが発生しますが、そのあとで画像やスタイルシートなどすべてのリソースの読み込みが完了すると load イベントが発生します。これを利用して、ヘッダー部分に JavaScript のコードを書いてみる。
![](https://blogimg.goo.ne.jp/user_image/52/28/9fab33e02b5782d8134cc71935dc2acf.jpg)
load イベントが発生すると、イベントハンドラとして登録したコードが実行され、ターゲットの要素の取得と click イベントへのイベントハンドラの登録が実行されます。
サンプル
「onload.html」ファイルを書く
![](https://blogimg.goo.ne.jp/user_image/74/61/a1f1d82ace55381fe9c50e645bb380ad.jpg)
外部のjsファイルを書く
![](https://blogimg.goo.ne.jp/user_image/0e/a1/2e5015c86d691c1f5d3148679cd10e4e.jpg)
ブラウザを立ち上げ、ボタンをクリックする
![](https://blogimg.goo.ne.jp/user_image/3f/40/b71dd878f4755f43f711469b3aec4a7b.jpg)
【scriptタグでdefer属性を設定する】------
この方法は JavaScript のコードを記述した外部のファイルを読み込む場合にだけ利用出来る。
![](https://blogimg.goo.ne.jp/user_image/30/c9/34b1721bf255ab11259b0cbc393d6db6.jpg)
defer 属性を設定された script タグで読み込まれた外部の JavaScript のファイルに記述されたコードは、すぐに実行されるのではなく DOM ツリーの構築が完了し DOMContentLoaded イベントが発生する直前に実行されます。
サンプル
![](https://blogimg.goo.ne.jp/user_image/62/bf/ba730745aa53840587db3207ec695a28.jpg)
jsファイル
![](https://blogimg.goo.ne.jp/user_image/09/34/e7ec9932780938e7398dfd44ffa633ba.jpg)
ブラウザを立ち上げて、ボタンをクリックする
![](https://blogimg.goo.ne.jp/user_image/2b/27/4e94708cc000560485178681f8201def.jpg)