为什么拆分<脚本>标签时使用document.write()?

为什么有些网站(或给客户提供javascript代码的广告商)采用在document.write()调用中分离<script>和/或</script>标记的技术?

我注意到亚马逊也这样做,例如:

<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>
137167 次浏览

我认为是为了防止浏览器的HTML解析器解释<script>,主要是</script>作为实际脚本的结束标签,但我不认为使用文档。写是一个很好的想法来评估脚本块,为什么不使用DOM…

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

</script>必须被分解,否则它将过早地结束封闭的<script></script>块。实际上,它应该在</之间分割,因为脚本块(根据SGML)应该是由任何结束标记打开(ETAGO)序列(即</)终止:

虽然STYLE和SCRIPT元素使用CDATA作为它们的数据模型,但是对于这些元素,用户代理必须以不同的方式处理CDATA。标记和实体必须被视为原始文本,并原样传递给应用程序。字符序列“</”(结束标记打开分隔符)的第一次出现被视为终止元素内容的结束。在有效的文档中,这将是元素的结束标记。

然而,实际上浏览器只在实际的</script>关闭标记上结束对CDATA脚本块的解析。

在XHTML中,脚本块没有这样的特殊处理,因此它们中的任何<(或&)字符必须像任何其他元素一样是&escaped;。然而,将XHTML解析为老式HTML的浏览器将会感到困惑。有一些涉及CDATA块的变通方法,但最简单的方法是避免使用这些未转义的字符。从适用于任何类型解析器的script中编写一个script元素的更好方法是:

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

下面是我在不需要任何形式的转义而想要内联生成脚本标记时使用的另一种变体:

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

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

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的条件加载来演示用例。希望这对某些人有用!

Javascript字符串字面值中的</script>被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>