【问题标题】:Why split the <script> tag when writing it with document.write()?为什么在用 document.write() 编写时拆分 <script> 标签?
【发布时间】:2022-12-13 12:06:56
【问题描述】:

为什么某些网站(或为客户提供 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('

    【讨论】:

    • // 的有效转义序列,那么为什么不使用它来代替 &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 中,它仍然是格式错误。你也可以使用 in inline event handler attributes where &lt; would also be invalid in both HTML and XHTML, so it has a wider applicability: if I were choosing one, easily-automated way to escape sensitive characters in JS string literals for all contexts, that's the one I'd go for.
    • 在 HTML 中,&lt; 可用于内联事件处理程序属性。 html5.validator.nu/… 关于 an sich, but since XHTML doesn’t support document.write (or innerHTML) anyway I don’t see how that’s relevant. 的 XHTML 兼容性,你是对的
    • @MathiasBynens—document.write 无关紧要,它恰好是示例。 OP 可以使用 innerHTML,它是关于隐藏标记解析器中的 &lt;/ 字符序列,无论它出现在哪里。只是大多数解析器在严格来说不应该在脚本元素中容忍它(但 HTML 解析器非常容忍)。尽管 &lt;/ 在所有情况下都足以满足 HTML,但您是正确的。
    • 我不认为转义开头 < 是必要的....document.write('&lt;script src="foo.js"&gt;&gt;') 似乎在所有回到 IE6 的浏览器中都足够了。 (我省略了 type 属性,因为它在 HTML5 中不是必需的,也不是任何浏览器都强制要求的。)
    【解决方案2】:

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

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

    我认为是为了防止浏览器的 HTML 解析器解释 <script>,主要是将 </script> 解释为实际脚本的结束标记,但是我不认为使用 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(<script>) 时,我的网站显示空白屏幕。这奏效了。
    • @dmp 除非从协议为 file:// 的文件系统运行
    猜你喜欢
    • 2010-09-19
    • 2012-10-19
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 2016-11-03
    • 2015-12-10
    相关资源
    最近更新 更多