用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>

Answers:


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 2011年

11
@Mathias:<\/script>在这种情况下可以,但是只能在HTML中使用;在没有额外的CDATA节包装的XHTML中,它仍然是格式错误。另外,您还可以使用\x3C内联事件处理程序属性,<这些属性在HTML和XHTML中都将无效,因此它具有更广泛的适用性:如果我选择了一种易于自动化的方法来在所有上下文中转义JS字符串文字中的敏感字符,那就是我要去的那个。
bobince 2011年

3
在HTML中,<可以在内联事件处理程序属性中使用。html5.validator.nu/…而且您对\x3Csich 的XHTML兼容性是正确的,但是由于XHTML 无论如何都不支持document.write(或innerHTML),所以我看不到它的意义。
Mathias Bynens 2011年

2
@MathiasBynens — document.write无关紧要,它恰好是示例。OP可以使用innerHTML,这是</在标记解析器中隐藏字符序列(无论发生在何处)的方法。只是大多数解析器在严格不应该的情况下才在脚本元素中容忍它(但是HTML解析器非常容忍)。您是正确的,尽管<\/在所有情况下都足以满足HTML要求。
RobG 2012年

3
我认为转义<开头不是必需的...。document.write('<script src="foo.js">\x3C/script>')在回到IE6的所有浏览器中似乎就足够了。(我省略了type属性,因为HTML5不需要该属性,也不是任何浏览器都必须执行。)
Matt Browne 2013年

34

这是我想要生成内联脚本标记(以便立即执行)而无需任何形式的转义时使用的另一种变化:

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

(注意:与网络上的大多数示例相反,我type="text/javascript"既未设置封闭标签,也未设置生成的标签:没有浏览器没有将其作为默认标签,因此这是多余的,但也不会受到损害,如果您不同意)。


好点再说:类型。从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
有必要防止解析器过早关闭脚本块...
使

9

Bobince发布的解决方案非常适合我。我也想为将来的访客提供一种替代方法:

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

救了我!使用document.write(<script>)时,我的网站显示空白屏幕。这工作了。
Tomas Gonzalez

@dmp除外,从协议为file://的文件系统运行时除外
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不是函数。
尼拉夫·扎维里

1
抱歉,我的错误:我写的append不是appendChild。更正了答案。感谢您的关注!
Mathieu Rodic

1
这看起来比手动拆分字符串要笼统得多。例如,如果字符串是由模板引擎插入的,则拆分是不可行的。
w1th0utnam3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.