こんにちは、ライターのマサトです!
初めてJavaScriptでDOMを扱うようになってくると、実行タイミングがズレてエラーになることがよくあります。
この場合、ほとんどの理由がHTML(DOM)のレンダリングとタイミングが合っていないことに起因します。これを上手く解消するには、HTML内の書く場所に注意したり、「onloadイベント」の正しい書き方を知る必要があります。
しかしながら、「実行タイミング」についてはいろんな罠が潜んでいるため、この記事で正しい知識を身につけてスッキリと理解できるようにしておきましょう。
JavaScript初心者におすすめの 無料プログラミングサイト3選
1位:侍テラコヤ | 2位:Progate | 3位:ドットインストール | |
---|---|---|---|
サイト名 | |||
学べる内容 | JavaScript、HTML/CSS、jQuery、Bootstrap、PHP、Laravel、Ruby、Ruby on Rails、Python、Java、WordPress、Cloud9、Git、AWS 、ChatGPT、Webデザインなど | JavaScript、HTML/CSS、PHP、Ruby、Python、Java、Go、Git、SQL など | JavaScript、HTML/CSS、Ruby、Python、Java、C言語 など |
質問 相談サポート | |||
練習問題の数 | |||
特徴 | 現役エンジニアとのマンツーマンレッスン 回答率100%のQ&A掲示板が使い放題 実践的なスキルを身につけられる課題機能 | 環境構築をしなくても学べる イラスト付きのスライド式教材 スマホアプリでも学べて手軽 | レッスン動画でインプット可能 3分の簡単な動画で手軽に学べる 有料プランなら質問ができる |
詳細 | 公式サイト | 公式サイト | 公式サイト |
これからJavascriptのスキルを向上させていきたい方に向けて、おすすめのJavascript学習サイト16選を別で紹介しているので、そちらもぜひ参考にしてみてください。
プログラミング初心者は学習サイトを登録するのがおすすめ
初心者が効率的にプログラミングを学習するには、プログラミング学習サイトを使うのがおすすめです。
無料で使用できるものであれば、余計な費用をかけることなく効率的に勉強することができ、他の人よりも早くスキルアップすることが可能になります。
無料で現場で活躍しているプロのエンジニアに相談ができる学習サイトなどもあるため、気になった方はぜひ利用してみるとよいでしょう。
【罠その1】JavaScriptは書く場所によってエラーになる!
まずは基本的なことですが、HTMLのソースコードは上から下へ順番に読み込まれて解析されていきます。
当然ながら、HTMLの中にはJavaScriptも存在しており、どの場所に書くかによって結果が変化することがあります。
JavaScriptを書く場所としては、大きく分けると「head要素内」と「body要素内」の2箇所があります。
基本的には「body要素内」にJavaScriptを書くことが多いのですが、その理由も合わせてもう少し詳しく見ていきましょう!
「head要素内」に書く場合
まずは、「head要素内」に書く場合について見ていきます。
基本的な書き方としては、以下のとおりです!
<html> <head> <!-- ここからJavaScriptファイルの読み込み --> <script src="jquery.js"></script> <script src="bootstrap.js"></script> <script> var items = ['ねこ', 'いぬ', 'とり']; for(var i=0; i<items.length; i++) { console.log(items[i]); } </script> </head> <body> <div id="container"> <h1>サンプルページ</h1> </div> </body> </html>
上記の例では、「jquery.js」や「bootstrap.js」などの外部ファイルを読み込んだり、「scriptタグ」内に直接コードを書いています。
このように、「head要素内」にJavaScriptを書くことはできますが、すべての読み込みが完了しないとDOMがレンダリングされないのでWebページの表示が遅くなってしまうというデメリットがあります。
\ 現役エンジニアと1on1レッスンが可能!/
公式サイトで詳細を見る
「body要素内」に書く場合
次に、「body要素内」にJavaScriptを書く場合について見ていきます。
先ほどのコードを利用すると、以下のとおりになります。
<html> <head> </head> <body> <div id="container"> <h1>サンプルページ</h1> </div> <!-- ここからJavaScriptファイルの読み込み --> <script src="jquery.js"></script> <script src="bootstrap.js"></script> <script> var items = ['ねこ', 'いぬ', 'とり']; for(var i=0; i<items.length; i++) { console.log(items[i]); } </script> </body> </html>
ポイントはbodyの閉じタグの直前に記述するという点です。
こうすることで先にDOMのレンダリング始まり、Webページが表示された後にJavaScriptが読み込まれるので、体感的に速く表示されると感じるわけです。
このような理由から、基本的にJavaScriptは「body要素内」に書くことが多い傾向にあります。
HTMLの中で読み込むJavaScriptのファイルには自分で作成したファイルの他に外部の人が作成したファイルを読み込む場合など様々なケースがあります。
そのあたりの詳しい内容は以下の記事にてご紹介していますので、ぜひ参考にしてみてください。
JavaScriptがエラーになる書き方
先ほどは「書く場所」について学びましたが、今度は「書き方」についてもう少し見ていきましょう!
例えば、よくある間違いとして「head要素内」から「pタグ」のDOMを取得しようとした事例がコチラ!
<head> <script> var memo = document.getElementById('memo'); memo.textContent = 'こんにちは!'; </script> </head> <body> <p id="memo"></p> </body>
このコードはエラーになります。
理由は冒頭でも書きましたが、HTMLは上から下へ順番に読み込まれて解析されるからです。
つまり、JavaScriptを読み込んだ時点では、まだ「pタグ」が存在していないのに文字列を代入しようとしているため、「pタグなんてないよ!」というエラーになるわけです。
そこで、「body要素内」に同じコードを書いてみましょう!
<head> </head> <body> <p id="memo"></p> <script> var memo = document.getElementById('memo'); memo.textContent = 'こんにちは!'; </script> </body>
今度はエラーになりません。
なぜなら、JavaScriptが読み込まれる前にDOMがレンダリングされており、「pタグ」が存在しているからですね。
ただし、bodyの閉じタグ直前に書いたからといって、必ずDOMのレンダリングが完了していることは保証されていません。
そのため、DOMを扱う場合には次の項目以降でご紹介する「onloadイベント」を利用するのがベストな選択となります!
\ 現役エンジニアと1on1レッスンが可能!/
公式サイトで詳細を見る
【罠その2】onloadイベントは書き方次第で実行されなくなる!
先ほどの項目で、JavaScriptの「書く場所」「書き方」について学びました。
そして、DOMを扱うのであれば、最終的に「onloadイベント」を利用するのがベストな選択肢になるわけですが、こちらも書き方について注意するべきポイントがあるので詳しく見ていきましょう!
window.onloadについて
まずは、「onloadイベント」の基本的な書き方について学んでいきましょう。
一般的には「window.onload」に関数を代入するカタチで書いていきます。
window.onload = function() { // 実行したい処理を書く }
このように書くことで、すべてのDOMツリー構造及び関連リソースが読み込まれたあとにJavaScriptが実行されるようになります。
つまり、「head要素内」でDOMを扱うプログラムを書いてもエラーにはなりません。(もちろん「body要素内」でもOK)
<head> <script> window.onload = function() { var memo = document.getElementById('memo'); memo.textContent = 'こんにちは!'; } </script> </head> <body> <p id="memo"></p> </body>
上記コードは、普通に書くとエラーになりかねませんが、「onloadイベント」を利用することで正常に動作します。
また、「bodyタグ」に直接「onloadイベント」を記述することも可能です!
<head> </head> <!-- bodyにonloadイベントを設定 --> <body onload="start()"> <p id="memo"></p> <script> function start() { var memo = document.getElementById('memo'); memo.textContent = 'こんにちは!'; } </script> </body>
あらかじめJavaScriptで関数を作成しておき、それを「bodyタグ」の「onloadイベント」で実行すれば良いわけです。
このように「onloadイベント」を覚えると、JavaScriptの「書く場所」を意識しなくてもDOMを扱えるようになるので非常に便利ですね!
\ 現役エンジニアと1on1レッスンが可能!/
公式サイトで詳細を見る
複数のwindow.onloadを書いた場合
「onloadイベント」はとても便利なのですが、注意するべきポイントもあります。
それは、複数の「onloadイベント」書いてしまうと上書きされてしまい、最後に書いたイベントだけが実行されるという仕様によるものです。
例えば、次のように書いた場合、「コンソールログ」には何が表示されるでしょうか?
window.onload = function() { console.log('ミカン'); } window.onload = function() { console.log('バナナ'); } window.onload = function() { console.log('メロン'); }
正解は、「メロン」です!
「window.onload」は関数を代入するという特性上、最後に代入された関数が上書きされて実行されるわけです。これは、簡単に言うと「変数」に「値」を代入する行為に似ているでしょう。
このような特性があることによって、複数人でプログラムを書いている場合に誤って「onloadイベント」を上書きしてしまうリスクがあるので注意が必要です。
そこで、このようなリスクを考慮しないイベント処理の書き方が存在しており、それが「addEventListener()」を使った方法です!
先ほどのコードを「addEventListener()」に置き換えるとこうなります!
window.addEventListener('load', function() { console.log('リンゴ'); }) window.addEventListener('load', function() { console.log('バナナ'); }) window.addEventListener('load', function() { console.log('メロン'); })
コンソールログには…
リンゴ
バナナ
メロン
という感じで、すべて出力されているのが分かります!
つまり、複数の「onloadイベント」を登録してもすべて実行されるようになっているわけですね。
【罠その3】onloadイベントは意外に遅い!
これまでに「onloadイベント」として、「window.onload」と「addEventListener()」の2種類を学んできました。
どちらにしても、「onloadイベント」はDOMのレンダリングやCSS・画像などの関連リソースをすべて読み込んだ後に実行される処理でした。
しかしながら、大抵の場合はDOMの「ツリー構造」さえ完了していればJavaScriptからDOMを扱えるので、すべてのリソースを読み込むまで待つ必要はないはずです。
そこで、知っておくと便利に使えるのが「DOMContentLoadedイベント」です。これはDOMの「ツリー構造」が完了した時点で実行されるので、通常の「onloadイベント」よりも速く動作するというメリットがあります。
一般的な書き方は次のとおり!
window.addEventListener('DOMContentLoaded', function() { // 実行したい処理を書く })
書き方は「onloadイベント」によく似ていますね。
それでは、先ほどのサンプルコードに「DOMContentLoaded」を使ってみましょう!
window.addEventListener('load', function() { console.log('リンゴ'); }) window.addEventListener('load', function() { console.log('バナナ'); }) window.addEventListener('DOMContentLoaded', function() { console.log('メロン'); })
最後のイベント処理だけ「DOMContentLoaded」を使っていますが、出力結果はどうなると思いますか?
正解は以下のとおり!
メロン
リンゴ
バナナ
という順番で表示されます。
つまり、「メロン」のイベント処理だけが「DOMContentLoaded」を利用しているので、最も速く実行されたというわけです!
\ 現役エンジニアと1on1レッスンが可能!/
公式サイトで詳細を見る
【罠その4】画像の読み込みには時間がかかる!
さて、これまではDOMを扱った事例を見てきましたが、少しステップアップした応用編として、今度は「画像ファイル」の扱いについて見ていきましょう!
JavaScriptから画像を読み込んで処理するケースにおいて、実はDOMを扱う事例と同じような現象が発生します。このあたりの事情について、知っているのと知らないのではかなり差がつくのでしっかりと学んでおきましょう!
基本的な画像の読み込み方!
まずは、JavaScriptから画像ファイルを読み込む方法について見ていきましょう!
一般的な書き方としては、「Image()」コンストラクタを使ってインスタンス化する方法です。
var img = new Image(); // インスタンス化 img.src = 'image/sample.jpg'; // 画像ファイルの読み込み document.body.appendChild(img); // 画像を表示
上記3行のコードで、画像を読み込んで表示させることが出来ます。
「img.src」に画像のパスを代入した時点で、ファイルの読み込みが自動的に始まるわけですが、画像ファイルは容量が大きいものが多いので読み込み完了までにある程度の時間が必要です。
この現象は、DOMのツリー構造が完了するまで待たないといけなかったケースとよく似ていると言えます。
例えば、読み込んだ画像の「幅サイズ」を取得するためのメソッドに「img.width」がありますが、次のように書いてしまうと取得することができません。
var img = new Image(); img.src = 'image/sample.jpg'; // 画像の「幅サイズ」を出力 console.log(img.width); document.body.appendChild(img);
上記コードの場合、コンソールログに出力されるのは「0」になります。つまり、幅サイズを取得できていないということになります。
これは、「img.src」に画像のパスが代入されて読み込みが開始されているものの、まだ読み込みが完了していないうちに「img.width」が実行されているので、正確なサイズが取得できていないことになります。
これを解消するには、やはり「onloadイベント」が必要になってくるわけですね。
\ 現役エンジニアと1on1レッスンが可能!/
公式サイトで詳細を見る
onloadイベントを使ってみよう!
DOMを扱った時と同じように、画像を扱う場合でも「onloadイベント」を使うことができます。
基本的な使い方は次のとおりです!
var img = new Image(); img.onload = function() { // 画像の読み込みが完了した時に実行する処理 }; img.src = 'image/sample.jpg';
このように、「img.onload」を使うことで、意図的に読み込みが完了した時の処理を書くことが出来るわけです。
先ほどの「img.width」による幅サイズの取得は、次のように書けば上手く実行されます。
var img = new Image(); img.onload = function() { // 画像の幅サイズを出力 console.log(img.width); }; img.src = 'image/sample.jpg'; document.body.appendChild(img);
このように書くことで、コンソールログには画像の幅サイズがしっかりと出力できるわけです。
ちなみに、「img.onload」も関数を代入するという性質上、複数書いた場合に上書きされるため、「addEventListener」を使った書き方にすることも可能です!
var img = new Image(); img.addEventListener('load', function { // 画像の幅サイズを出力 console.log(img.width); }; img.src = 'image/sample.jpg'; document.body.appendChild(img);
上記の画像ファイルの読み込みの他にもExcelやCSVファイルの読み込みやJSONファイルと呼ばれるプログラムで処理しやすいように加工されたファイルを読み込むなど、様々なファイルを読み込むケースがあると思います。
そのような方法については以下の記事で詳しく紹介していますので、ぜひ参考にしてください。
\ 現役エンジニアと1on1レッスンが可能!/
公式サイトで詳細を見る
実はonloadイベントの逆パターンもある!
これまで、さまざまな「onloadイベント」について学習してきました。
このイベントは、基本的に「何らかの処理を待つ」ことで、実行タイミングを調整していたわけですが、逆に「何らかの処理に移行」しようとした時に実行するイベントも用意されているのでご紹介しておきます!
これは主に、Webページをリロードする際や、別のURLに遷移する時などに実行されるイベントで、離脱防止や誤ってリロードするのを防止するような目的で使われることが多いでしょう。
「onbeforeunload」の使い方について
まずは基本的な書き方について見ていきたいのですが、利用するのは「onbeforeunload」と呼ばれるイベント処理です。
一般的な使い方としては、これまでの「onloadイベント」とまったく同じで、書き方も2種類あります。
// 1つ目の書き方 window.onbeforeunload = function(e) { // 実行したい処理を書く } // 2つ目の書き方 window.addEventListener('beforeunload', function(e) { // 実行したい処理を書く });
「onbeforeunload」は、Webページの離脱防止に使われることから、次のように書くことで「メッセージダイアログ」をポップアップ表示することができます。
window.addEventListener('beforeunload', function(e) { // メッセージを表示する e.returnValue = '本当にページ移動しますか?'; });
ただし、一部のブラウザは固定のメッセージのみ表示されるようになっています。
メッセージ表示例
jQueryのreadyとloadの違いと使い方
ページの読み込み時に処理を行うには、jQueryの「ready」と「load」を使う方法もあります。
jQueryのreadyとloadの違いと使い方についてはこちらの記事で詳しく解説しているので、ぜひ確認してください。
まとめ
今回はJavaScriptの実行タイミングについて、主に「onloadイベント」に潜むいくつかの罠をまとめて紹介いたしました。
簡単にもう一度まとめるとこうなります!
- DOMのレンダリング前にJavaScriptを実行しない
- window.onloadは上書きされることに注意する
- 「DOMContentLoaded」でさらに素早く実行できる
- DOMと同じく画像ファイルも読み込み完了までに時間がかかる
- 「onbeforeunload」は「リロード・画面遷移」の時に実行できる
これらは決して難しい内容ではないうえ、知っているとWeb開発をさらに効率よく進められる要素でもあるので、ぜひこの機会にマスターしておきましょう!