初心者編|無料コーディング教材|HTML・CSS・jQueryを実践で学ぶ

HTMLでJavaScriptを読み込む方法|defer・async・moduleも初心者向けに解説

HTMLとJavaScriptのコードを接続し、ブラウザで実行するイメージ

HTMLからJavaScriptを読み込むには、script要素のsrc属性へJavaScriptファイルのパスを指定します。ただし、書く場所やdeferasyncの選び方を間違えると、「ファイルはあるのに動かない」「HTML要素を取得できない」といった問題が起こります。

この記事では、初心者がそのまま試せる外部ファイルの読み込み方から、HTMLへ直接書く方法、読み込み順序、JavaScriptモジュール、エラーの確認方法まで解説します。

HTMLからJavaScriptを読み込む基本

通常のWebページでは、HTMLとJavaScriptを別ファイルに分け、HTMLのhead内からdefer付きで読み込む方法が使いやすいです。まず、同じフォルダへindex.htmlscript.jsを用意します。

sample-site/
├── index.html
└── script.js

index.htmlへ、ボタンと結果を表示する要素を作ります。script要素はhead内へ書き、src="script.js"で外部ファイルを指定してください。

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JavaScriptの読み込み練習</title>
    <script src="script.js" defer></script>
  </head>
  <body>
    <button class="message-button" type="button">メッセージを表示</button>
    <p class="result"></p>
  </body>
</html>

次に、script.jsへ処理を書きます。ボタンをクリックすると、空だった段落へメッセージが入ります。

const button = document.querySelector('.message-button');
const result = document.querySelector('.result');

button.addEventListener('click', () => {
  result.textContent = 'JavaScriptを読み込めました!';
});

ブラウザでindex.htmlを開き、ボタンを押して「JavaScriptを読み込めました!」と表示されれば成功です。deferがあるため、ブラウザはHTMLの解析を進めながらファイルを取得し、HTMLの解析が終わった後にJavaScriptを実行します。

script要素とsrc属性の役割

script要素は、HTMLへ実行可能なスクリプトを組み込むための要素です。src属性はsourceの略で、読み込む外部ファイルの場所をURLとして指定します。

<script src="script.js" defer></script>

外部ファイルを読み込むscript要素にも終了タグ</script>が必要です。<script src="script.js" />のような自己終了タグにはしないでください。

JavaScriptファイルのパスを正しく指定する

JavaScriptが読み込めない原因として多いのが、srcのパス間違いです。パスは、基本的にscript要素を書いたHTMLファイルの場所を基準に考えます。

HTMLとJavaScriptが同じフォルダにある場合

<script src="script.js" defer></script>

./script.jsと書いても同じ場所を表せます。最初の./は「現在のフォルダ」という意味です。

jsフォルダの中にある場合

sample-site/
├── index.html
└── js/
    └── script.js
<script src="js/script.js" defer></script>

ファイル名の大文字と小文字も区別して確認しましょう。開発中のパソコンでは動いても、公開先のサーバーでScript.jsscript.jsが別ファイルとして扱われることがあります。

相対パスと絶対パスの考え方はCSSファイルの読み込みでも共通です。パス指定から練習したい方は、次の記事も参考にしてください。

script・defer・async・moduleの違い

同じ外部JavaScriptでも、script要素の属性によってダウンロードと実行のタイミングが変わります。HTML要素を操作する一般的なスクリプトなら、まずdeferを選ぶと理解しやすいです。

通常のscript、defer、async、JavaScriptモジュールの読み込みと実行順序を比較した図
上から通常のscript、defer、async、type=”module”の流れです。通常はHTML解析を止め、deferは解析後、asyncは取得直後、moduleは依存ファイルを取得して解析後に実行します。
書き方HTML解析中の取得実行タイミング実行順序主な用途
属性なし解析を止めて取得取得後すぐ記述順即時実行が必要な古典的スクリプト
defer解析と並行HTML解析後記述順DOMを操作する一般的な外部スクリプト
async解析と並行取得でき次第保証されない他の処理へ依存しないスクリプト
type="module"解析と並行HTML解析後が基本依存関係を解決import・exportを使うモジュール

属性なしのscriptはHTML解析を止める

asyncdefertype="module"のない外部スクリプトに出会うと、ブラウザはHTMLの解析を一時停止し、ファイルを取得して実行してから続きを解析します。

<script src="script.js"></script>

この書き方をhead内で使い、JavaScriptが後にあるHTML要素を探すと、まだ解析されていないためnullになることがあります。ファイルの取得中もHTML解析が止まるため、通常は理由なく選びません。

deferはHTML解析後に記述順で実行する

deferを付けた外部スクリプトはHTML解析と並行して取得され、解析が終わってから実行されます。複数ある場合は、HTMLに書いた順序で実行されます。

<script src="vendor.js" defer></script>
<script src="script.js" defer></script>

script.jsvendor.jsの機能を使う場合でも、この順序を保てます。また、HTML解析後に実行されるため、外部ファイル内で通常はDOMContentLoadedを重ねて待つ必要がありません。

asyncは取得できたファイルから実行する

asyncもHTML解析と並行してファイルを取得しますが、取得が終わるとすぐに実行します。複数ファイルの実行順序は保証されません。

<script src="independent-tool.js" async></script>

ほかのJavaScriptやDOMに依存せず、取得でき次第動いてよい処理に向いています。順番が重要なライブラリと自作コードへ、まとめてasyncを付けるのは避けましょう。

type=”module”はimport・exportを使える

JavaScriptを機能ごとのファイルへ分けるなら、type="module"を使えます。モジュールスクリプトは既定で遅延実行されるため、deferを追加する必要はありません。

<script type="module" src="js/main.js"></script>
export function createMessage(name) {
  return `こんにちは、${name}さん!`;
}
import { createMessage } from './message.js';

const result = document.querySelector('.result');
result.textContent = createMessage('田中');

モジュール内の相対パスでは、./message.jsのように./と拡張子まで書きます。モジュールをローカルで試すときは、file://で直接開かず、開発用のローカルサーバーを使うとCORSやMIMEタイプの問題を避けやすくなります。

body終了タグ直前へscriptを書く方法

script要素を</body>の直前へ置く方法も動作します。その時点では前にあるHTML要素の解析が終わっているため、DOMを取得できます。

  <button class="message-button" type="button">メッセージを表示</button>
  <script src="script.js"></script>
</body>

ただし、「JavaScriptは必ずbodyの最後」と決まっているわけではありません。head内へdefer付きで書けば、HTML解析を妨げず早い段階からファイル取得を始められ、読み込み場所もまとまります。

bodyの最後へ置く理由とDOMの読み込み順序は、次の記事でも実例を使って解説しています。

HTMLへJavaScriptを直接書く方法

短い動作確認なら、script要素の中へJavaScriptを直接書くインラインスクリプトも使えます。この場合はsrc属性を付けません。

<button class="message-button" type="button">メッセージを表示</button>
<p class="result"></p>

<script>
  const button = document.querySelector('.message-button');
  const result = document.querySelector('.result');

  button.addEventListener('click', () => {
    result.textContent = '読み込みに成功しました!';
  });
</script>

インラインスクリプトへdeferを付けても、通常のスクリプトでは効果がありません。head内に直接書きつつDOMの解析を待つ必要がある場合は、DOMContentLoadedイベントを利用できます。

<script>
  document.addEventListener('DOMContentLoaded', () => {
    const result = document.querySelector('.result');
    result.textContent = 'HTMLの解析が終わりました。';
  });
</script>

実際の制作では、HTMLと処理を整理し、複数ページから再利用しやすくするため、外部ファイルへ分けるのが基本です。HTML内へ直接書く具体例はこちらでも確認できます。

JavaScriptを読み込めないときの確認方法

Consoleで最初のエラーを確認する

Chromeではページ上で右クリックして「検証」を選び、DevToolsの「Console」を開きます。赤いエラーが複数ある場合は、後続エラーの原因になりやすい最初のエラーから確認してください。

Cannot read properties of nullと表示される場合は、セレクタの書き間違いか、対象HTMLより先にJavaScriptが実行されている可能性があります。クラス名を確認し、外部スクリプトならdeferを付けましょう。

NetworkでJavaScriptのHTTPステータスを確認する

DevToolsの「Network」を開いてページを再読み込みし、対象の.jsファイルを探します。404ならパスやファイル名が間違っています。200でも動かない場合は、Consoleの構文エラーやMIMEタイプを確認します。

JavaScriptの先頭で読み込みを確認する

ファイルそのものが実行されているか分からないときは、script.jsの先頭へ一時的にconsole.log()を書きます。

console.log('script.jsを読み込みました');

Consoleへ表示されればファイルの取得と実行までは成功しています。確認後は、不要になったログを削除してください。

defer=”false”と書いていないか

deferasyncは真偽属性です。属性が存在すれば有効になるため、defer="false"と書いても無効にはなりません。使わない場合は属性自体を削除します。

ライブラリより先に自作コードを実行していないか

jQueryなどのライブラリを使う場合は、ライブラリを先、自作コードを後に読み込みます。順序が必要なら両方へdeferを使い、asyncは使いません。

<script src="js/library.js" defer></script>
<script src="js/app.js" defer></script>

CDNからjQueryを読み込む具体的な方法と注意点は、次の記事で解説しています。

HTMLでJavaScriptを読み込むときのよくある質問

script要素はheadとbodyのどちらに書きますか?

一般的な外部JavaScriptなら、head内へdefer付きで書く方法が分かりやすいです。古典的な属性なしスクリプトを使う場合は、DOMより後に実行するため</body>直前へ置く方法もあります。

type=”text/javascript”は必要ですか?

通常のJavaScriptを読み込むだけなら、type="text/javascript"は省略できます。ESモジュールとして扱う場合は、type="module"を指定します。

deferとasyncを両方付けるとどうなりますか?

両方を指定すると、対応ブラウザではasyncとして扱われます。実行順序が必要ならdeferだけを使い、独立して取得後すぐに動いてよい場合だけasyncを選びましょう。

まとめ

HTMLからJavaScriptを読み込む基本は、script要素のsrc属性へファイルのパスを書くことです。DOMを操作する一般的な外部スクリプトなら、head内でdeferを付けると、HTML解析後に記述順で実行できます。

ほかの処理に依存しないスクリプトにはasyncimportexportを使う構成にはtype="module"を選びます。動かないときは、パスを見直し、DevToolsのConsoleとNetworkを順番に確認してみてください。

参考資料