HTML
この記事では、JavaScriptなどのスクリプトをHTML文書へ組み込む<script>要素と、表示を止めにくくするdefer・asyncの使い分けを初心者向けに説明します。
この記事でわかること
- 外部・インラインJavaScriptを読み込む基本
defer・async・type="module"の違い- 実行順序、CSP、外部スクリプトの安全な扱い
The HTML script tag is used to incorporate JavaScript code, which adds dynamic features and behaviors to a web page.
script要素
<script> とは?
HTMLの <script> タグは、Webページに動的な振る舞いを追加するために使用されます。これにより、ページ上でユーザーとのインタラクションが可能になり、静的な文書をはるかに興味深く有用なものに変えることができます。一般的に、JavaScript(JS)というプログラミング言語がこの目的のために使われます。
基本は <head> に書いて defer または type="module" を付けます(表示を止めず、実行順も安定します)。async は順序不要の計測タグなどに向きます。
<script>タグの基本
<script> タグを使用する基本的な方法は、直接 JavaScriptコードを HTML内に書き込むか、外部の JavaScriptファイルを参照するかの二つがあります。直接書き込む場合は、<script> タグの内部に JavaScriptコードを記述します。外部ファイルを参照する場合は、src 属性を使用してファイルの場所を指定します。
src 属性で JavaScriptの外部 jsファイルを読み込む場合には、例えば以下のように記述します。
HTML
<script src="sample.js"></script>
インラインで JavaScript を書くときは、そのまま <script> の中に記述します。HTMLコメントで囲む必要はありません。
HTML
<script>
console.log("Hello, script!");
</script>
セキュリティ面の考慮
Web開発では、セキュリティは極めて重要です。スクリプトの使用にあたっては、クロスサイトスクリプティング(XSS)のような攻撃から保護するため、信頼できるコードのみを使用し、ユーザー入力を適切にサニタイズすることが重要です。また、可能な限り HTTPS経由で外部スクリプトを読み込むことを推奨します。
属性
- src属性
- 外部スクリプトファイルの URLを指定する。
- type属性
- スクリプトの言語タイプやデータ形式を指定するために使用されます。
- charset属性
- 外部スクリプトファイルの文字エンコーディングを指定する。
- async属性
- 利用可能な時点でスクリプトを実行する。
- defer属性
- HTML文書を読み込んだ時点でスクリプトを実行する。
src属性
src 属性は、外部のスクリプト・ファイルのアドレスを指定します。src 属性を指定した場合は、script 要素の中に、インラインでコードを記述する事はできません。
type 属性を使ってスクリプトのタイプを指定した場合、src 属性を使って読み込まれるスクリプトのプログラム言語の種類と一致しなければいけません。
type 属性を指定しない場合は、そのスクリプト・ファイルのプログラム言語は JavaScriptでなければいけません。
src属性のマークアップ例
HTML
<script src="example.js"></script>
type属性
type 属性は、主にスクリプトの言語タイプやデータ形式を指定するために使用されます。しかし、HTML5以降では、JavaScriptがデフォルトのスクリプト言語となっているため、type 属性を指定しなくても JavaScriptとして解釈されます。それでも、特定のケースや古いブラウザの互換性、またはスクリプトがデータブロックとして扱われるべき場合に type 属性が利用されることがあります。
以下は、HTMLの <script> 要素で使用される type 属性の値と、それぞれの簡単な説明です。
- (type は省略が既定)
- JavaScriptを読み込むだけなら
typeは書かなくてOK。HTML標準の既定でJavaScriptとして扱われます。 - module
type="module"はモジュールを読み込みます。HTMLの読み込み後に実行(実質deferと同じタイミング)、import/exportが使え、ファイルごとにスコープが分かれ、トップレベルawaitも使えます。古いブラウザ向けにnomoduleの併用も可能です。- text/javascript, text/ecmascript, application/javascript, application/ecmascript
- いずれも歴史的な表現です。通常は
typeを書かずに読み込みます(サーバは適切なMIMEタイプを返す必要があります)。 - text/plain
- 実行しない “データ置き場” として使えます。ブラウザはスクリプトとしても画面表示としても扱いません。見せたい場合は
<pre><code>を使うか、別のJSで取り出して描画します。 - カスタムMIMEタイプ(例: application/x-my-custom-type)
- 特定のスクリプト処理システムやライブラリ用にカスタマイズされた MIMEタイプを使用することもあります。これらは通常、データブロックとしてスクリプトを埋め込む際や、特定の処理をトリガーするために用いられます。
現代の Web開発では、特に指定する必要がない場合は type 属性を省略し、JavaScriptコードとしての解釈を前提とするのが一般的です。また、モジュールスクリプトを利用する際には type="module" を使用することが推奨されます。
なお、旧来から使われてきた language 属性は、HTML5では非準拠となりますので注意してください。
charset属性
今日では推奨されません。外部スクリプトの文字エンコーディングは、ファイル自体をUTF-8で保存し、サーバのHTTPヘッダ(Content-Type)で示すのが基本です。charset は互換目的以外では使わないでOKです。
※ どうしても古い環境で文字化けする場合の最終手段として使われてきましたが、現在はファイルをUTF-8に統一し、サーバ側の設定を整える方が確実です。
async属性
async 属性は、src 属性で指定された外部のスクリプトファイルを非同期で読み込んで、そのスクリプトが実行できるようになったらすぐに実行されるよう指示するための論理属性です。この属性は、src 属性が指定されている場合にのみ利用可能です。
async属性の利用例
HTML
<script src="example.js" async></script>
ふつうの <script src> は、HTMLの読み込み(パース)をいったん止めて、スクリプトをダウンロード→実行します。
一方で async を付けると、ダウンロード中はパースを止めず、ダウンロードが終わった時点でただちに実行します(実行中は一時的にパースが止まることがあります)。そのぶん表示は軽くなりますが、他のスクリプトとの実行順は保証されません。
async 属性はHTML5で導入され、現在は主要ブラウザで広く対応しています。
defer属性
defer 属性は、HTMLの読み込み(パース)が終わったタイミングで、スクリプトを書いた順に実行するための論理属性です。この属性は、src 属性が指定されている場合にのみ利用可能です(<script>...</script> 直書きでは不可)。
defer属性の利用例
HTML
<script src="example.js" defer></script>
では、いくつかサンプルを見てみましょう。ここでは、次の JavaScriptの外部ファイルを使います。
example.js
document.getElementById("st").innerHTML = "読み込み完了";
サンプルコード
HTML
<script src="example.js"></script>
<div id="st">ページロード中...</div>
このサンプルは、JavaScriptエラーになります。なぜなら、example.js は、div 要素がブラウザに読み込まれる前に実行されてしまうからです。
次のように、script 要素に defer属性を指定してみましょう。
defer属性を入れたサンプルコード
HTML
<script src="example.js" defer></script>
<div id="st">ページロード中...</div>
この場合は、ページのロードが完了してから example.js が実行されるため、JavaScriptエラーになる事はありません。
この defer 属性は、onload イベント・ハンドラ属性の代わりとして利用する事が可能ですが、JavaScriptが実行されるタイミングは若干異なります。defer 属性が指定された script 要素のスクリプトは、ドキュメントから load イベントが発生する前に実行されます。そのため、onload で指定されたスクリプトより先に実行されることになります。
互換性: 現在は主要ブラウザで広くサポートされています。defer は外部スクリプトで有効になり、HTMLのパースが終わったあとに書いた順で実行され、DOMContentLoaded より前に動きます。なお、type="module" のスクリプトは既定で同様のタイミング(実質 defer 相当)で動きます。
<script type="module"> について
<script type="module">は、複数のJSファイルを“輸入(import)・輸出(export)”しながら組み立てる現代的な書き方です。<script defer>と同じく、HTMLのパースを邪魔せず後で実行されます(=描画ブロックしにくい)。- モジュール内で
exportした関数や定数を、別ファイルでimportして再利用できます。 - モジュールは自動でstrict mode、ファイルごとに独立したスコープ、CORSでの取得など、セキュアで予測しやすいふるまいになります。
- 追加で便利な要素:動的インポート(必要なときだけ読み込む)、トップレベル
await(モジュールの先頭でawait)、import maps(パスの短縮・エイリアス)など。
スタートガイド(最短で動かす)
まずは2ファイル作成
math.js と main.js を用意して、HTMLから main.js をモジュールとして読み込みます。
Hello Modules
HTML
<p>Hello Modules</p>
<script type="module" src="./main.js"></script>
math.js
JavaScript
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
main.js
JavaScript
import { add, PI } from './math.js';
const result = add(2, 3);
console.log('2 + 3 =', result);
console.log('PI =', PI);
ポイント
- 相対パスは拡張子
.jsを含めるのが基本(CDNやビルド環境を使わない素のブラウザ運用なら特に重要)。 <script type="module">はdefer相当なので、</body>直前に置かなくてもOK。- それぞれのJSはモジュールスコープ。
varやfunctionを書いてもwindowには漏れません。
nomodule フォールバック(旧ブラウザ配慮)
ほぼ不要な時代ですが、レガシー向けにクラシック版を用意するならこうします。
HTML
<script type="module" src="/modern/main.js"></script>
<script nomodule src="/legacy/bundle.legacy.js"></script>
モジュール対応ブラウザは上だけ実行、非対応は下だけ実行。
