script 要素

4.11.1 script 要素

カテゴリー:
メタデータ・コンテント
フロー・コンテント
フレージング・コンテント
スクリプトサポート要素
この要素を使うことができるコンテキスト:
メタデータ・コンテントが期待される場所
フレージング・コンテントが期待される場所
スクリプトサポート要素が期待される場所
コンテントモデル:
src 属性がなければ、type 属性の値に依存します。ただし、スクリプトコンテント制限に一致しなければいけません。
src 属性があれば、この要素は空、もしくは、スクリプトコンテント制限にも一致するスクリプト説明文しか入れることができません。
コンテント属性:
グローバル属性
src - リソースのアドレス
type - 組込リソースのタイプ
charset - 外部スクリプトリソースの文字エンコーディング
async - スクリプトを非同期に実行する
defer - スクリプトの実行を遅らせる
crossorigin - この要素がクロスオリジンのリクエストをどう扱うか
text/html におけるタグの省略:
どちらのタグも省略できません。
指定可能な ARIA role 属性 の値:
なし
指定可能な ARIA ステートとプロパティ属性:
グローバル aria-* 属性
DOM インタフェース:
interface HTMLScriptElement : HTMLElement {
           attribute DOMString src;
           attribute DOMString type;
           attribute DOMString charset;
           attribute boolean async;
           attribute boolean defer;
           attribute DOMString crossOrigin;
           attribute DOMString text;
};

script 要素は、ウェブ制作者がドキュメントに動的なスクリプトやデータブロックを含めることができるようにします。この要素は、ユーザーのためのコンテンツを表しません。

動的スクリプトを入れるために使う場合、インラインでスクリプトを組み込むか、または、src 属性を使って外部ファイルからスクリプトをインポートすることができます。スクリプト言語が "text/javascript" に相当するものでない場合、下記の通り、type 属性は必須です。どんな言語が使われるにしても、script 要素のコンテンツは、その言語仕様の要件に適合しなければいけません。

データブロックを入れるために使う場合(スクリプトとは対照的ですが)、そのデータはインラインで組み込まなければいけません。そして、type 属性を使ってデータのフォーマットを指定しなければいけません。この場合、src 属性を指定してはいけません。そして、script 要素のコンテンツは、採用フォーマットに定義された要件に適合しなければいけません。

type 属性は、スクリプトの言語やデータのフォーマットを与えます。この属性が存在すれば、その値は妥当な MIME タイプでなければいけません。charset パラメータを指定してはいけません。デフォルトは、この属性がない場合に使われますが、"text/javascript" です。

src 属性は、指定する場合、利用する外部スクリプトのリソースのアドレスを与えます。この属性値は、type 属性が存在するなら、それによって指定されたタイプの、存在しなければ、"text/javascript" タイプのスクリプトのリソースを識別するスペースで潜在的に囲まれた空でない妥当な URL でなければいけません。もし、そのタイプがスクリプト言語を指し、そのリソースがその言語仕様の要件に適合しているなら、そのリソースは指定タイプのリソースになります。

charset 属性は、外部スクリプトのリソースの文字エンコーディングを与えます。この属性は、src 属性がない場合は、指定してはいけません。もしこの属性がセットされた場合、その値はエンコーディングラベルの 1 つに一致しなければいけません。大文字と小文字は区別されません。また、その値は、外部ファイルの Content-Type メタデータcharset パラメータがあれば、それと同じエンコーディングを指定しなければいけません。 [ENCODING]

asyncdefer 属性は、どのようにスクリプトを実行すべきかを指し示す論理属性です。deferasync 属性は、src 属性が存在しない場合は、指定してはいけません。

これらの属性を使って選択しうるモードが 3 つ考えられます。async 属性があれば、スクリプトは利用可能になった時点で非同期に実行されます。async 属性がなく、defer 属性があれば、スクリプトはページのパースが終了したときに実行されます。どちらの属性もなければ、ユーザーエージェントがページのパースを継続する前に、即座にスクリプトが取り出され実行されます。

これらの属性の正確な処理の詳細は、主に歴史的な理由から、やや重要であり、HTML のいろいろな観点を巻き込んでいます。実装の要件は、それゆえに、やむを得ず、この仕様の全体にわたって散らばっています。下記(このセクション)のアルゴリズムは、この処理の中核を説明しています。しかし、これらのアルゴリズムは、HTML や外部コンテンツXML における script開始タグ終了タグに対するパース規則、document.write() メソッドの規則、スクリプティングのハンドリングなどによって、参照したり、参照されたりします。

defer 属性は、たとえ async 属性が指定されていても、指定することができます。この場合、defer しかサポートしない(async をサポートしない)古いウェブブラウザーは、デフォルトである同期ブロッキング動作の代わりに、defer 挙動にフォールバックすることになります。

crossorigin 属性は、CORS セッティング属性です。これは、他のオリジンから取得されたスクリプトに対して、エラー情報を見せるかどうかを制御します。

動的に src, type, charset, async, defer, crossorigin 属性を変更しても、直接的には何も起こりません。これらの属性は、後述しますが、ある特定の時にしか使われません。

script 要素は、いくつかの関連の状態を持ちます。

一つ目は、スクリプトのブロックが "開始済み" かどうかを表すフラグです。最初は、script 要素はこのフラグをセットしてはいけません(スクリプトのブロックは、生成されるとき、"開始済み" ではありません)。script 要素の複製手順では、複製元に "開始済み" フラグがセットされているなら、複製先にもセットしなければいけません。

二つ目は、該当の要素が "パーサー挿入" だったかどうかを表すフラグです。最初は、script 要素はこのフラグをセットしてはいけません。これは、HTML パーサーXML パーサーによって、挿入そしてその要素の処理に影響を及ぼす script にセットされます。

三つ目は、該当の要素が "強制非同期" になるかどうかを表すフラグです。最初は、script 要素はこのフラグをセットしなければいけません。これは、HTML パーサーXML パーサーが、挿入する script にセットします。さらに、"強制非同期" フラグがセットされた script 要素に async コンテント属性が追加されたら、この要素の "強制非同期" フラグは解除されなければいけません。

四つ目は、スクリプトのブロックが "パーサー実行準備済み" かどうかを表すフラグです。最初は、script 要素はこのフラグをセットしてはいけません(スクリプトのブロックは、生成されるとき、"パーサー実行準備済み" ではありません)。このフラグは、"パーサー挿入" にもなっている要素に対してのみ、パーサーに該当のスクリプトをいつ実行すべきかを知らせるために使われます。

最後は、スクリプトブロックのタイプスクリプトブロックの文字エンコーディングスクリプトブロックのフォールバック文字エンコーディング です。これらは、スクリプトが準備できたときに、その時点での要素の属性と script 要素の Document に基づいて決定されます。

"パーサー挿入" であるとしてマークされていない script 要素が次のリストに挙げられたイベントの 1 つに遭遇したら、ユーザーエージェントは、その script 要素を同期で準備しなければいけません:

スクリプトを準備するために、ユーザーエージェントは次の通りに動作しなければいけません:

  1. If the script element is marked as having "already started", then the user agent must abort these steps at this point. The script is not executed.

  2. If the element has its "parser-inserted" flag set, then set was-parser-inserted to true and unset the element's "parser-inserted" flag. Otherwise, set was-parser-inserted to false.

    This is done so that if parser-inserted script elements fail to run when the parser tries to run them, e.g. because they are empty or specify an unsupported scripting language, another script can later mutate them and cause them to run again.

  3. If was-parser-inserted is true and the element does not have an async attribute, then set the element's "force-async" flag to true.

    This is done so that if a parser-inserted script element fails to run when the parser tries to run it, but it is later executed after a script dynamically updates it, it will execute asynchronously even if the async attribute isn't set.

  4. If the element has no src attribute, and its child nodes, if any, consist only of comment nodes and empty Text nodes, then the user agent must abort these steps at this point. The script is not executed.

  5. If the element is not in a Document, then the user agent must abort these steps at this point. The script is not executed.

  6. If either:

    • the script element has a type attribute and its value is the empty string, or
    • the script element has no type attribute but it has a language attribute and that attribute's value is the empty string, or
    • the script element has neither a type attribute nor a language attribute, then

    ...let the script block's type for this script element be "text/javascript".

    Otherwise, if the script element has a type attribute, let the script block's type for this script element be the value of that attribute with any leading or trailing sequences of space characters removed.

    Otherwise, the element has a non-empty language attribute; let the script block's type for this script element be the concatenation of the string "text/" followed by the value of the language attribute.

    The language attribute is never conforming, and is always ignored if there is a type attribute present.

  7. If the user agent does not support the scripting language given by the script block's type for this script element, then the user agent must abort these steps at this point. The script is not executed.

  8. If was-parser-inserted is true, then flag the element as "parser-inserted" again, and set the element's "force-async" flag to false.

  9. The user agent must set the element's "already started" flag.

    The state of the element at this moment is later used to determine the script source.

  10. If the element is flagged as "parser-inserted", but the element's Document is not the Document of the parser that created the element, then abort these steps.

  11. If scripting is disabled for the script element, then the user agent must abort these steps at this point. The script is not executed.

    The definition of scripting is disabled means that, amongst others, the following scripts will not execute: scripts in XMLHttpRequest's responseXML documents, scripts in DOMParser-created documents, scripts in documents created by XSLTProcessor's transformToDocument feature, and scripts that are first inserted by a script into a Document that was created using the createDocument() API. [XHR] [DOMPARSING] [DOM]

  12. If the script element has an event attribute and a for attribute, then run these substeps:

    1. Let for be the value of the for attribute.

    2. Let event be the value of the event attribute.

    3. Strip leading and trailing whitespace from event and for.

    4. If for is not an ASCII case-insensitive match for the string "window", then the user agent must abort these steps at this point. The script is not executed.

    5. If event is not an ASCII case-insensitive match for either the string "onload" or the string "onload()", then the user agent must abort these steps at this point. The script is not executed.

  13. If the script element has a charset attribute, then let the script block's character encoding for this script element be the result of getting an encoding from the value of the charset attribute.

    Otherwise, let the script block's fallback character encoding for this script element be the same as the encoding of the document itself.

    Only one of these two pieces of state is set.

  14. If the element has a src content attribute, run these substeps:

    1. Let src be the value of the element's src attribute.

    2. If src is the empty string, queue a task to fire a simple event named error at the element, and abort these steps.

    3. Resolve src relative to the element.

    4. If the previous step failed, queue a task to fire a simple event named error at the element, and abort these steps.

    5. Do a potentially CORS-enabled fetch of the resulting absolute URL, with the mode being the current state of the element's crossorigin content attribute, the origin being the origin of the script element's Document, and the default origin behaviour set to taint.

      The resource obtained in this fashion can be either CORS-same-origin or CORS-cross-origin. This only affects how error reporting happens.

      For performance reasons, user agents may start fetching the script (as defined above) as soon as the src attribute is set, instead, in the hope that the element will be inserted into the document (and that the crossorigin attribute won't change value in the meantime). Either way, once the element is inserted into the document, the load must have started as described in this step. If the UA performs such prefetching, but the element is never inserted in the document, or the src attribute is dynamically changed, or the crossorigin attribute is dynamically changed, then the user agent will not execute the script so obtained, and the fetching process will have been effectively wasted.

  15. Then, the first of the following options that describes the situation must be followed:

    If the element has a src attribute, and the element has a defer attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attribute

    The element must be added to the end of the list of scripts that will execute when the document has finished parsing associated with the Document of the parser that created the element.

    The task that the networking task source places on the task queue once the fetching algorithm has completed must set the element's "ready to be parser-executed" flag. The parser will handle executing the script.

    If the element has a src attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attribute

    The element is the pending parsing-blocking script of the Document of the parser that created the element. (There can only be one such script per Document at a time.)

    The task that the networking task source places on the task queue once the fetching algorithm has completed must set the element's "ready to be parser-executed" flag. The parser will handle executing the script.

    If the element does not have a src attribute, and the element has been flagged as "parser-inserted", and either the parser that created the script is an XML parser or it's an HTML parser whose script nesting level is not greater than one, and the Document of the HTML parser or XML parser that created the script element has a style sheet that is blocking scripts

    The element is the pending parsing-blocking script of the Document of the parser that created the element. (There can only be one such script per Document at a time.)

    Set the element's "ready to be parser-executed" flag. The parser will handle executing the script.

    If the element has a src attribute, does not have an async attribute, and does not have the "force-async" flag set

    The element must be added to the end of the list of scripts that will execute in order as soon as possible associated with the Document of the script element at the time the prepare a script algorithm started.

    The task that the networking task source places on the task queue once the fetching algorithm has completed must run the following steps:

    1. If the element is not now the first element in the list of scripts that will execute in order as soon as possible to which it was added above, then mark the element as ready but abort these steps without executing the script yet.

    2. Execution: Execute the script block corresponding to the first script element in this list of scripts that will execute in order as soon as possible.

    3. Remove the first element from this list of scripts that will execute in order as soon as possible.

    4. If this list of scripts that will execute in order as soon as possible is still not empty and the first entry has already been marked as ready, then jump back to the step labeled execution.

    If the element has a src attribute

    The element must be added to the set of scripts that will execute as soon as possible of the Document of the script element at the time the prepare a script algorithm started.

    The task that the networking task source places on the task queue once the fetching algorithm has completed must execute the script block and then remove the element from the set of scripts that will execute as soon as possible.

    Otherwise
    The user agent must immediately execute the script block, even if other scripts are already executing.

外部スクリプトをフェッチしたら、そのリソースがフェッチ(前記に定義)されてネットワーキング・タスクソースによってキューイングされたタスクが実行されるまで、該当の要素のドキュメントの load イベントを遅らせなければいけません。

Document保留中パーシング・ブロッキング・スクリプトは、その Document のパーサーによって使われます。

If a script element that blocks a parser gets moved to another Document before it would normally have stopped blocking that parser, it nonetheless continues blocking that parser until the condition that causes it to be blocking the parser no longer applies (e.g. if the script is a pending parsing-blocking script because there was a style sheet that is blocking scripts when it was parsed, but then the script is moved to another Document before the style sheet loads, the script still blocks the parser until the style sheets are all loaded, at which time the script executes and the parser is unblocked).

ユーザーエージェントがスクリプトブロックを実行する必要になったとき、次の手順を実行しなければいけません:

  1. If the element is flagged as "parser-inserted", but the element's Document is not the Document of the parser that created the element, then abort these steps.

  2. Jump to the appropriate set of steps from the list below:

    If the load resulted in an error (for example a DNS error, or an HTTP 404 error)

    Executing the script block must just consist of firing a simple event named error at the element.

    If the load was successful

    Executing the script block must consist of running the following steps. For the purposes of these steps, the script is considered to be from an external file if, while the prepare a script algorithm above was running for this script, the script element had a src attribute specified.

    1. Initialize the script block's source as follows:

      If the script is from an external file and the script block's type is a text-based language

      The contents of that file, interpreted as a Unicode string, are the script source.

      To obtain the Unicode string, the user agent run the following steps:

      1. If the resource's Content Type metadata, if any, specifies a character encoding, and the user agent supports that encoding, then let character encoding be that encoding, and jump to the bottom step in this series of steps.

      2. If the algorithm above set the script block's character encoding, then let character encoding be that encoding, and jump to the bottom step in this series of steps.

      3. Let character encoding be the script block's fallback character encoding.

      4. If the specification for the script block's type gives specific rules for decoding files in that format to Unicode, follow them, using character encoding as the character encoding specified by higher-level protocols, if necessary.

        Otherwise, decode the file to Unicode, using character encoding as the fallback encoding.

        The decode algorithm overrides character encoding if the file contains a BOM.

      If the script is from an external file and the script block's type is an XML-based language

      The external file is the script source. When it is later executed, it must be interpreted in a manner consistent with the specification defining the language given by the script block's type.

      If the script is inline and the script block's type is a text-based language

      The value of the text IDL attribute at the time the element's "already started" flag was last set is the script source.

      If the script is inline and the script block's type is an XML-based language

      The child nodes of the script element at the time the element's "already started" flag was last set are the script source.

    2. Fire a simple event named beforescriptexecute that bubbles and is cancelable at the script element.

      If the event is canceled, then abort these steps.

    3. If the script is from an external file, then increment the ignore-destructive-writes counter of the script element's Document. Let neutralized doc be that Document.

    4. Create a script, using the script block's source, the URL from which the script was obtained, the script block's type as the scripting language, and the script settings object of the script element's Document's Window object.

      If the script came from a resource that was fetched in the steps above, and the resource was CORS-cross-origin, then pass the muted errors flag to the create a script algorithm as well.

      This is where the script is compiled and actually executed.

    5. Decrement the ignore-destructive-writes counter of neutralized doc, if it was incremented in the earlier step.

    6. Fire a simple event named afterscriptexecute that bubbles (but is not cancelable) at the script element.

    7. If the script is from an external file, fire a simple event named load at the script element.

      Otherwise, the script is internal; queue a task to fire a simple event named load at the script element.

IDL 属性 src, type, charset, defer は、それぞれ同じ名前のコンテント属性を反映しなければいけません。

crossOrigin IDL 属性は、既知の値に限定して、crossorigin コンテント属性を反映しなければいけません。

async IDL 属性は、該当の要素が非同期に実行するかどうかを制御します。この要素の "強制非同期" フラグがセットされているなら、取得時においては、async IDL 属性は true を返さなければいけません。セット時においては、まず "強制非同期" フラグを false にセットして、この IDL 属性の新たな値が false なら、そのコンテント属性は削除されなければならず、そして、この IDL 属性の新たな値が true なら、空の文字列がセットされなければいけません。この要素の "強制非同期" フラグがセットされていないなら、この IDL 属性は async コンテント属性を反映しなければいけません。

script . text [ = value ]

要素のコンテンツを返します。Text ノードではない子ノードは無視されます。

値を指定することで、要素の子を置き換えることができます。

IDL 属性 text は、script 要素の子となるすべての Text ノードのコンテンツをツリー順に連結したものを返さなければいけません(コメントや要素などのノードは無視されます)。値が指定されたら、textContent IDL 属性と同じように動作しなければいけません。

document.write()メソッドを使って挿入された場合は、script 要素は(通常は同期的に)実行します。しかし、innerHTMLouterHTML 属性を使って挿入された場合は、何も実行しません。

この例では、2 つの script 要素が使われています。一方は外部スクリプトを組み込み、もう一方はいくらかのデータを含んでいます。

<script src="game-engine.js"></script>
<script type="text/x-game-map">
........U.........e
o............A....e
.....A.....AAA....e
.A..AAA...AAAAA...e
</script>

この場合のデータは、スクリプトがビデオゲームの地図を生成するために使うかもしれません。しかし、そのデータを、そのように使わなければいけないというわけではありません。恐らく、その地図データは、実際にはページのマークアップの他の部分に組み込まれているかもしれません。そして、ここにあるデータブロックは、そのゲーム地図内の特定の特徴を探すユーザーを助けるために、サイトの検索エンジンによって使われるだけなのかもしれません。

次のサンプルは、後にドキュメントの別の箇所で使われる関数を定義するために、どうやって script 要素を使うのかを示しています。また、ドキュメントがパースされている最中にスクリプトを呼び出すために、script 要素をどうやって使うのかを示しています。この例では、フォームの出力を初期化しています。

<script>
 function calculate(form) {
   var price = 52000;
   if (form.elements.brakes.checked)
     price += 1000;
   if (form.elements.radio.checked)
     price += 2500;
   if (form.elements.turbo.checked)
     price += 5000;
   if (form.elements.sticker.checked)
     price += 250;
   form.elements.result.value = price;
 }
</script>
<form name="pricecalc" onsubmit="return false" onchange="calculate(this)">
 <fieldset>
  <legend>Work out the price of your car</legend>
  <p>Base cost: £52000.</p>
  <p>Select additional options:</p>
  <ul>
   <li><label><input type=checkbox name=brakes> Ceramic brakes (£1000)</label></li>
   <li><label><input type=checkbox name=radio> Satellite radio (£2500)</label></li>
   <li><label><input type=checkbox name=turbo> Turbo charger (£5000)</label></li>
   <li><label><input type=checkbox name=sticker> "XZ" sticker (£250)</label></li>
  </ul>
  <p>Total: £<output name=result></output></p>
 </fieldset>
 <script>
  calculate(document.forms.pricecalc);
 </script>
</form>
4.11.1.1 スクリプティング言語

スクリプトブロックのタイプが、ユーザーエージェントが実装しているスクリプト言語の MIME タイプ文字列にある関連コンポーネントに一致するなら、ユーザーエージェントはスクリプト言語をサポートしているといいます。大文字と小文字は区別しません

ユーザーエージェントが認識しなければならない MIME タイプ文字列と、それらが参照する言語は以下のとおりです:

"application/ecmascript"
"application/javascript"
"application/x-ecmascript"
"application/x-javascript"
"text/ecmascript"
"text/javascript"
"text/javascript1.0"
"text/javascript1.1"
"text/javascript1.2"
"text/javascript1.3"
"text/javascript1.4"
"text/javascript1.5"
"text/jscript"
"text/livescript"
"text/x-ecmascript"
"text/x-javascript"
JavaScript. [ECMA262]

ユーザーエージェントは、他の言語に対する MIME タイプをサポートすることができます。しかし、上記リストの言語に対して別の MIME タイプをサポートしてはいけません。ユーザーエージェントは、上記にリストされた言語をサポートする必要はありません。

次の MIME タイプ(パラメータがあるかないかに関わらず)は、スクリプティング言語として解釈されなければいけません:

  • "text/plain"
  • "text/xml"
  • "application/octet-stream"
  • "application/xml"

これらのタイプは、データブロックのフォーマットして使われがちではっきりと定義されていないタイプゆえ、ここで明示的に列挙しています。もしこれらが突然にユーザーエージェントによってスクリプトだと解釈されるようになってしまったら問題になるからです。

サポートする言語かどうかをタイプから決定するとき、ユーザーエージェントは、未知の MIME パラメータを無視してはいけません。タイプは、すべてのパラメータを含めて比較されるものです。

たとえば、charset パラメータを含むタイプは、上記にリストされたスクリプティング言語の参照として認識されないでしょう。

4.11.1.2 script 要素のコンテンツの制限

このセクションで説明した奇妙な制限を避ける最も簡単で安全な方法は、常に "<!--" を "<\!--" として、"<script" を "<\script" として、そして、"</script" を "<\/script" として、これらのシーケンスがスクリプトの中のリテラルに現れた時にエスケープすることです(たとえば、文字列や正規表現やコメント)。そして、そのような構造を使うコードを書かないようにすることです。そうすることで、このセクションの制約が起こってしまいがちな危険を避けることができます。つまり、歴史的な事情ゆえに、この HTML の script ブロックのパースは、これらのシーケンスに直面すると直感に反するような挙動になってしまう、奇妙でエキゾチックな慣行になっているのです。

script 要素の textContent は、次の ABNF にある script プロダクション、および、Unicode の文字セットに一致しなければいけません。 [ABNF]

script        = outer *( comment-open inner comment-close outer )

outer         = < any string that doesn't contain a substring that matches not-in-outer >
not-in-outer  = comment-open
inner         = < any string that doesn't contain a substring that matches not-in-inner >
not-in-inner  = comment-close / script-open

comment-open  = "<!--"
comment-close = "-->"
script-open   = "<" s c r i p t tag-end

s             =  %x0053 ; U+0053 LATIN CAPITAL LETTER S
s             =/ %x0073 ; U+0073 LATIN SMALL LETTER S
c             =  %x0043 ; U+0043 LATIN CAPITAL LETTER C
c             =/ %x0063 ; U+0063 LATIN SMALL LETTER C
r             =  %x0052 ; U+0052 LATIN CAPITAL LETTER R
r             =/ %x0072 ; U+0072 LATIN SMALL LETTER R
i             =  %x0049 ; U+0049 LATIN CAPITAL LETTER I
i             =/ %x0069 ; U+0069 LATIN SMALL LETTER I
p             =  %x0050 ; U+0050 LATIN CAPITAL LETTER P
p             =/ %x0070 ; U+0070 LATIN SMALL LETTER P
t             =  %x0054 ; U+0054 LATIN CAPITAL LETTER T
t             =/ %x0074 ; U+0074 LATIN SMALL LETTER T

tag-end       =  %x0009 ; "tab" (U+0009)
tag-end       =/ %x000A ; "LF" (U+000A)
tag-end       =/ %x000C ; "FF" (U+000C)
tag-end       =/ %x0020 ; U+0020 SPACE
tag-end       =/ %x002F ; "/" (U+002F)
tag-end       =/ %x003E ; ">" (U+003E)

script 要素がスクリプト説明文を含むときは、次のセクションで説明するとおり、さらに追加の制限が、この要素のコンテンツに課せられます。

次のスクリプトは、この問題を説明しています。次の通り、文字列を含んだスクリプトがあるとします:

var example = 'この文字列を考えます: <!-- <script>';
console.log(example);

script のブロックに直接的にこの文字列が入れられたとしたら、前述の制約に反することになります:

<script>
  var example = 'この文字列を考えます: <!-- <script>';
  console.log(example);
</script>

しかし、もっと大きな問題は、この制約に反する理由でもありますが、このスクリプトは、実際には、奇妙な形でパースされてしまうことです。この上記のスクリプトブロックは終端されていません。つまり、この断片にある "</script>" 終了タグのように見えるものは、実際には、まだこの script ブロックの一部です。このスクリプトが実行されることはありません(なぜなら、それは終端されていないからです)。もし仮にそれがどういうわけか実行されてしまったとしても、次に見せるマークアップであるかのように、失敗に終わるでしょう。なぜなら、そのスクリプト(ハイライト部分)は妥当な JavaScript ではないからです。

<script>
  var example = 'この文字列を考えます: <!-- <script>';
  console.log(example);
</script>
<!-- 見かけによらず、これはまだ script の一部です! -->
<script>
 ... // これもまだ同じ sctipt ブロックです...
</script>

ここで起こっていることは、レガシーな理由によるものです。HTML の中の script 要素にある "<!--" と "<script" という文字列は、パーサーがブロック終端を考えるために、バランスを取る必要があります。

このセクションの冒頭で言及されているとおり、問題を含んだ文字列をエスケープすることで、この問題は完全に回避されます:

<script>
  var example = 'この文字列を考えます: <\!-- <\script>';
  console.log(example);
</script>
<!-- これは script ブロックの間にあるただのコメントです。 -->
<script>
 ... // これは新らしい script ブロックです
</script>

これらのシーケンスは、次の例にあるとおり、スクリプト表現ではごく自然に起こりえます:

if (x<!--y) { ... }
if ( player<script ) { ... }

文字がエスケープできないようなケースでは、表現を次のように書き換えることで、このシーケンスが起こらないようにすることができます:

if (x < !--y) { ... }
if (!--y > x) { ... }
if (!(--y) > x) { ... }
if (player < script) { ... }
if (script > player) { ... }

こうすることで、別の落とし穴も避ける事ができます:関連の歴史的な事情により、JavaScript の中の文字列 "<!--" は、実際には、まさに "//" のような、行コメントの開始として扱われます。

4.11.1.3 外部スクリプトのインライン説明文

script 要素の src 属性が指定されたら、script 要素のコンテンツは、もしあれば、要素のコンテンツから引き出される text IDL 属性の値が、次の ABNF の documentation プロダクション、および、Unicode の文字セットに一致するようにしなければいけません。 [ABNF]

documentation = *( *( space / tab / comment ) [ line-comment ] newline )
comment       = slash star *( not-star / star not-slash ) 1*star slash
line-comment  = slash slash *not-newline

; characters
tab           = %x0009 ; "tab" (U+0009)
newline       = %x000A ; "LF" (U+000A)
space         = %x0020 ; U+0020 SPACE
star          = %x002A ; "*" (U+002A)
slash         = %x002F ; "/" (U+002F)
not-newline   = %x0000-0009 / %x000B-10FFFF
                ; a Unicode character other than "LF" (U+000A)
not-star      = %x0000-0029 / %x002B-10FFFF
                ; a Unicode character other than "*" (U+002A)
not-slash     = %x0000-002E / %x0030-10FFFF
                ; a Unicode character other than "/" (U+002F)

これは、JavaScript コメントの中に要素のコンテンツを差し込むことに相当します。

この要件は、さらに、script 要素のコンテンツの構文における前段の制限が加わります。

これは、ウェブ制作者が、外部スクリプトファイルを参照すると同時に、ドキュメントの中では、著作権情報や API 情報といった説明文を入れることができるようにしたものです。この構文は、src 属性も提供しているとはいえ、ウェブ制作者が誤って妥当なスクリプトのように見えるものを含めないよう、制約されます。

<script src="cool-effects.js">
 // create new instances using:
 //    var e = new Effect();
 // start the effect using .play, stop using .stop:
 //    e.play();
 //    e.stop();
</script>
4.11.1.4 script 要素と XSLT との相互作用

このセクションは非規定です。

本仕様は、XSLT が script 要素とどう作用し合うのかについては定義しません。しかし、実際にはこれを定義している仕様がないため、ここでは、実装者向けに、既存の実装に基づいて、いくつかのガイドラインを示します:

  • XSLT 変形プログラムが <?xml-stylesheet?> 処理命令によって呼び出され、ブラウザーが直接的な DOM 変形を実装しているなら、XSLT プロセッサーによって生成された script 要素は、"パーサー挿入" とマークされる必要があります。そして、ドキュメント順に実行する必要があります(modulo scripts marked defer or async)。一方、変形は非同期に行われます。

  • XSLTProcessor.transformToDocument() メソッドは、ブラウジングコンテキストにない Document に要素を追加します。そして、それに応じて、それらが生成するあらゆる script 要素は、スクリプト準備アルゴリズムにおいて、"開始済み" フラグ がセットされる必要があります。決して実行されることはありません(スクリプティングは無効です)。そのような script 要素は、なお、"パーサー挿入" とマークされている必要があります。しかし、async IDL 属性は async コンテント属性が無いため false を返します。

  • XSLTProcessor.transformToFragment() メソッドは、document.createElementNS() を使って要素を生成することによって、手動で生成されたものと同等であるフラグメントを生成する必要があります。例えば、それは、"パーサー挿入" ではなく、"開始済み" フラグもセットされていない script 要素を生成する必要があります。そうすることで、それらは、そのフラグメントがドキュメントに挿入されるときに実行することができます。

最初の 2 つの場合と最後の場合の間の大きな違いは、最初の 2 つは Document で動作するのに対し、最後のはフラグメントで動作するという点です。


※ 原文:http://www.w3.org/TR/2014/REC-html5-20141028/scripting-1.html#the-script-element