为什么在使用document.write()编写<script>标记时将其拆分?

Dan*_*ane 263 html javascript

为什么有些网站(或提供客户端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>
Run Code Online (Sandbox Code Playgroud)

bob*_*nce 366

</script>必须被打破,否则它会<script></script>过早地结束封闭区块.实际上它应该在<和之间分开/,因为假设一个脚本块(根据SGML)被任何结束标记打开(ETAGO)序列(即</)终止:

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

但实际上,浏览器只会在实际的</script>close-tag 上解析CDATA脚本块.

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

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

  • `/ /`是`/`的有效转义序列,那么为什么不使用它来代替`<`的字符串文字转义?例如`document.write('<script src = foo.js> <\/script>');`.此外,`</ script>`不是唯一可以关闭`<script>`元素的字符序列.这里有更多信息:http://mathiasbynens.be/notes/etago (29认同)
  • @Mathias:`<\/script>`在这种情况下很好,但它只适用于HTML; 在XHTML中没有额外的CDATA部分包装,它仍然是一个良好的形成错误.你也可以在内联事件处理程序属性中使用`\ x3C`,其中`<`在HTML和XHTML中也是无效的,因此它具有更广泛的适用性:如果我选择一种,轻松自动的方式来逃避JS中的敏感字符所有上下文的字符串文字,这就是我要去的那个. (11认同)
  • 在HTML中,`<`可用于内联事件处理程序属性.http://html5.validator.nu/?doc=data%3Atext%2Fhtml%3Bcharset%3Dutf-8%2C%3C!doctype+html%3E%3Ctitle%3E%3C%2Ftitle%3E%3Cp%2520contenteditable%2520oninput %3D%22alert('%3Clol%3E')%22%3E你对`\ x3C`的sHT的XHTML兼容性是正确的,但是因为XHTML不支持`document.write`(或`innerHTML` )无论如何,我不知道那是多么相关. (3认同)
  • 我不认为转换开头<是必要的....`document.write('<script src ="foo.js">\x3C/script>')`似乎足以在所有浏览器中回到IE6.(我省略了type属性,因为它在HTML5中不是必需的,也不是任何浏览器所要求的强制执行.) (3认同)
  • @ MathiasBynens -`document.write`是无关紧要的,它恰好就是一个例子.OP可以使用innerHTML,它隐藏着来自标记解析器的`</`字符序列,无论它出现在哪里.只是大多数解析器在严格意义上不应该在脚本元素内容忍它(但HTML解析器非常宽容).你是正确的虽然`<\ /`在所有情况下都适用于HTML. (2认同)

Sto*_*ffe 30

这是我想要生成内联脚本标记时使用的另一种变体(因此它立即执行),而不需要任何形式的转义:

<script>
    var script = document.createElement('script');
    script.src = '/path/to/script.js';
    document.write(script.outerHTML);
</script>
Run Code Online (Sandbox Code Playgroud)

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

  • 这个比接受的答案更好,因为这种变化实际上可以缩小.缩小后,此"x3C/script>"将变为"</ script>". (8认同)

CMS*_*CMS 20

我认为是为了防止浏览器的HTML解析器解释<script>,主要是</ script>作为实际脚本的结束标记,但我不认为使用document.write是评估脚本的绝佳主意块,为什么不使用DOM ...

var newScript = document.createElement("script");
...
Run Code Online (Sandbox Code Playgroud)

  • 有必要防止解析器过早关闭脚本块... (4认同)

Mat*_*dic 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>
Run Code Online (Sandbox Code Playgroud)

...但说实话,使用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>
Run Code Online (Sandbox Code Playgroud)


Jon*_*osi 8

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);
    })();
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我为jQuery包含了一个条件加载来演示用例.希望对某人有用!

  • 无需检测协议 - 无协议的URI工作得很好('//foo.com/bar.js'等) (3认同)
  • 也不需要设置异步.它为所有动态创建的脚本标记设置. (3认同)