document.write()で書き込むときに<script>タグを分割する理由


268

一部のサイト(またはクライアントにJavaScriptコードを提供する広告主)が、呼び出し内でタグ<script></script>タグを分割する手法を採用しているのdocument.write()はなぜですか?

アマゾンもこれを行うことに気づきました、例えば:

<script type='text/javascript'>
  if (typeof window['jQuery'] == 'undefined') document.write('<scr'+'ipt type="text/javascript" src="http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js"></sc'+'ript>');
</script>

回答:


373

</script>そうしないと、囲んでいる<script></script>ブロックが早く終了してしまうため、分割する必要があります。本当にそれが間に分割する必要があります<し、/スクリプトブロックがなければ(SGMLに応じて)想定しているので、任意の終了タグオープン(ETAGO)配列(すなわちによって終了します</

STYLE要素とSCRIPT要素はデータモデルにCDATAを使用しますが、これらの要素の場合、CDATAはユーザーエージェントによって異なる方法で処理する必要があります。マークアップとエンティティは、生のテキストとして扱われ、そのままアプリケーションに渡される必要があります。文字シーケンス " </"(終了タグの開始区切り文字)の最初の出現は、要素のコンテンツの終了を終了するものとして扱われます。有効なドキュメントでは、これは要素の終了タグになります。

ただし、実際には、ブラウザは実際の</script>終了タグでCDATAスクリプトブロックの解析を終了するだけです。

XHTMLでは、スクリプトブロックにはそのような特別な処理はありません。そのため、その中の<(または&)文字&escaped;は他の要素と同じである必要があります。ただし、XHTMLを古いHTMLとして解析しているブラウザーは混乱します。CDATAブロックに関連する回避策はありますが、これらの文字をエスケープせずに使用しないようにするのが最も簡単です。どちらのタイプのパーサーでも機能するスクリプトからスクリプト要素を記述するより良い方法は次のとおりです。

<script type="text/javascript">
    document.write('\x3Cscript type="text/javascript" src="foo.js">\x3C/script>');
</script>

30
\/はの有効なエスケープシーケンスです/。そのため、文字列リテラルエスケープの代わりにそれを使用しないのはなぜ<ですか?例えばdocument.write('<script src=foo.js><\/script>');。また、要素</script>を閉じることができる唯一の文字シーケンスではありません<script>。ここにいくつかの情報があります:mathiasbynens.be/notes/etago
Mathias Bynens '29

11
@Mathias:<\/script>この場合は問題ありませんが、HTMLでのみ機能します。余分なCDATAセクションの折り返しのないXHTMLでは、それはまだ整形式エラーです。また、HTMLとXHTMLの両方で無効になる\x3Cインラインイベントハンドラー属性を使用できるため、<適用範囲が広くなります。すべてのコンテキストでJS文字列リテラルの機密文字をエスケープする簡単な自動化方法を1つ選択した場合、それは私が行きたいもの。
ボビンス、2011年

3
HTMLでは、<インラインイベントハンドラー属性で使用できます。html5.validator.nu/…そして\x3C、sichのXHTML互換性についてはあなたは正しいですが、XHTMLはdocument.write(またはinnerHTML)をサポートしていないので、それがどのように関連しているかはわかりません。
Mathias Bynens、2011年

2
@ MathiasBynens— document.writeは無関係で、たまたま例にすぎません。OPはinnerHTMLを使用できたかもしれませんが</、マークアップパーサーから文字シーケンスが発生する場所はどこでも非表示にすることです。ほとんどのパーサーがスクリプト要素内で厳密に許容しない場合にのみ許容します(ただし、HTMLパーサーは非常に許容範囲が広いです)。それは正しいですが<\/、HTMLの場合はそれで十分です。
RobG

3
<をエスケープする必要はないと思います... document.write('<script src="foo.js">\x3C/script>')IE6に戻るすべてのブラウザで十分なようです。(type属性はHTML5では必須ではないため省略しました。また、どのブラウザーでも必須ではありません。)
Matt Browne

34

エスケープの形式を必要とせずにスクリプトタグをインラインで(すぐに実行されるように)生成するときに使用した別のバリエーションを次に示します。

<script>
    var script = document.createElement('script');
    script.src = '/path/to/script.js';
    document.write(script.outerHTML);
</script>

(注:ネット上のほとんどの例とは異なりtype="text/javascript"、囲みタグも生成されたタグも設定していません。デフォルトとしてそれを持たないブラウザはないため、冗長ですが、害はありません。同意しない場合)。


良い点re:タイプ。デフォルトはHTML5では「text / javascript」として定義されているため、役に立たない属性です。w3.org/html/wg/drafts/html/master/...
ルーク

8
このバリエーションは実際に縮小できるため、これは受け入れられた回答よりも優れています。この「x3C / script>」は、縮小化すると「</ script>」になります。
Zoltan Kochan 2015年

20

ブラウザのHTMLパーサーが<script>を解釈しないようにするためだと思いますが、主に</ script>を実際のスクリプトの終了タグとして解釈しますが、document.writeを使用することはスクリプトを評価するための優れたアイデアだとは思いませんブロック、DOMを使用しない理由...

var newScript = document.createElement("script");
...

4
パーサーがスクリプトブロックを途中で閉じないようにする必要があります...
roenving

9

ボビンスが投稿したソリューションは、私にとって完璧に機能します。私は将来の訪問者にも代替方法を提供したいと思いました:

if (typeof(jQuery) == 'undefined') {
    (function() {
        var sct = document.createElement('script');
        sct.src = ('https:' == document.location.protocol ? 'https' : 'http') +
          '://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js';
        sct.type = 'text/javascript';
        sct.async = 'true';
        var domel = document.getElementsByTagName('script')[0];
        domel.parentNode.insertBefore(sct, domel);
    })();
}

この例では、使用例を示すためにjQueryの条件付きロードを含めました。それが誰かに役立つことを願っています!


3
プロトコルを検出する必要はありません-プロトコルなしのURIは問題なく機能します( '//foo.com/bar.js'など)
dmp

3
非同期を設定する必要もありません。動的に作成されたすべてのスクリプトタグに対してオンに設定されます。
Noishe 2017年

助かりました!document.write(<script>)を使用すると、サイトに空白の画面が表示されました。これはうまくいきました。
Tomas Gonzalez

//:プロトコルはファイルになりますファイルシステムから実行している場合を除き、@dmp
mplungjan

9

</script>Javascriptの文字列litteral内部は(予期しない動作を引き起こす、終了タグとしてHTMLパーサーによって解釈さJSFiddle上の例を参照します)。

これを回避するには、コメントの間にJavaScriptを配置します(このスタイルのコーディングは、JavaScriptがブラウザー間で十分にサポートされていなかった頃から一般的でした)。これは動作します(JSFiddleの例を参照):

<script type="text/javascript">
    <!--
    if (jQuery === undefined) {
        document.write('<script type="text/javascript" src="http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js"></script>');
    }
    // -->
</script>

...しかし、正直なところ、使用することdocument.writeは私がベストプラクティスと考えるものではありません。DOMを直接操作しないのはなぜですか?

<script type="text/javascript">
    <!--
    if (jQuery === undefined) {
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script.setAttribute('src', 'http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js');
        document.body.appendChild(script);
    }
    // -->
</script>

1
純粋なJSを使用する場合でも、document.writeを使用する必要があります;)
Nirav Zaveri

すみません、書くつもりでした-これにはjQueryライブラリが必要ですよね?document.body.appendを使用すると、document.body.appendが関数ではないというエラーが発生しました。
Nirav Zaveri 2016

1
すみません、私の間違い:のappend代わりに書いたappendChild。答えを修正しました。お気づきありがとうございます!
Mathieu Rodic 2016

1
これは、文字列を手動で分割するよりもはるかに一般的に見えます。たとえば、文字列がテンプレートエンジンによって挿入された場合、分割は実行できません。
w1th0utnam3 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.