【问题标题】:Why split the <script> tag when writing it with document.write()?为什么用 document.write() 写 <script> 标签时要拆分它?
【发布时间】:2010-09-19 03:53:00
【问题描述】:

为什么某些网站(或向客户提供 javascript 代码的广告商)采用一种技术,将 &lt;script&gt; 和/或 &lt;/script&gt; 标记拆分到 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>

【问题讨论】:

    标签: javascript html


    【解决方案1】:

    &lt;/script&gt; 必须被分解,否则它会过早结束封闭的&lt;script&gt;&lt;/script&gt; 块。实际上它应该在&lt;/ 之间分开,因为脚本块(根据SGML)应该是terminated by any end-tag open (ETAGO) sequence (i.e. &lt;/)

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

    但实际上浏览器只在实际的 &lt;/script&gt; 关闭标签上解析 CDATA 脚本块。

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

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

    【讨论】:

    • \// 的有效转义序列,那么为什么不直接使用它来代替&lt; 的那些字符串文字转义呢?例如。 document.write('&lt;script src=foo.js&gt;&lt;\/script&gt;');。此外,&lt;/script&gt; 不是唯一可以关闭&lt;script&gt; 元素的字符序列。更多信息:mathiasbynens.be/notes/etago
    • @Mathias: &lt;\/script&gt; 在这种情况下很好,但它只适用于 HTML;在没有额外 CDATA 部分包装的 XHTML 中,它仍然是格式正确的错误。您还可以在内联事件处理程序属性中使用\x3C,其中&lt; 在 HTML 和 XHTML 中也无效,因此它具有更广泛的适用性:如果我选择一种易于自动化的方式来转义 JS 字符串中的敏感字符所有上下文的文字,这就是我想要的。
    • 在 HTML 中,&lt; 可用于内联事件处理程序属性。 html5.validator.nu/… 你说对了 \x3C 的 XHTML 兼容性是对的,但由于 XHTML 不支持 document.write(或 innerHTML),我不明白这有什么关系。
    • @MathiasBynens—document.write 无关紧要,它只是一个例子。 OP 可以使用 innerHTML,它是关于从标记解析器中隐藏 &lt;/ 字符序列,无论它出现在哪里。只是大多数解析器在严格不应该的情况下在脚本元素中容忍它(但 HTML 解析器非常容忍)。你是对的,尽管&lt;\/ 在所有情况下都适用于 HTML。
    • 我不认为转义开头 document.write('<script src="foo.js">\x3C/script>') 似乎在所有浏览器回到 IE6 中就足够了。 (我省略了 type 属性,因为它在 HTML5 中不是必需的,也不是任何浏览器所要求的。)
    【解决方案2】:

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

    <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/…
    • 这个比公认的答案更好,因为这种变化实际上可以被缩小。这个 'x3C/script>' 会在缩小后变成 ''。
    【解决方案3】:

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

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

    【讨论】:

    • 有必要防止解析器过早关闭脚本块...
    【解决方案4】:

    Javascript 字符串中的 &lt;/script&gt; 被 HTML 解析器解释为结束标记,导致意外行为 (see example on JSFiddle)。

    为避免这种情况,您可以将 javascript 放在 cmets 之间(这种编码方式很常见,早在浏览器对 Javascript 的支持很差时)。这会起作用(see example in 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>
    

    【讨论】:

    • 如果你想使用纯 JS,你仍然想使用 document.write ;)
    • 对不起,我的意思是写 - 这需要 jQuery 库,对吧?当我使用document.body.append时,它抛出了一个错误,即document.body.append不是一个函数。
    • 对不起,我的错误:我写的是append而不是appendChild。更正了答案。感谢您的关注!
    • 这看起来比手动拆分字符串更通用。例如。如果字符串是由模板引擎插入的,则拆分是不可行的。
    【解决方案5】:

    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 包含了一个条件加载来演示用例。希望这对某人有用!

    【讨论】:

    • 无需检测协议 - 无协议 URI 工作正常('//foo.com/bar.js' 等)
    • 也不需要设置异步。它为所有动态创建的脚本标签设置。
    • 救了我!使用 document.write(
    • @dmp 除非从文件系统运行,协议为 file://
    猜你喜欢
    • 2022-12-13
    • 2012-10-19
    • 1970-01-01
    • 2011-09-01
    • 2015-12-10
    • 1970-01-01
    • 2011-05-24
    • 1970-01-01
    • 2014-10-11
    相关资源
    最近更新 更多