【问题标题】:Stop IE from loading dynamically included script twice阻止 IE 两次加载动态包含的脚本
【发布时间】:2009-11-02 13:08:32
【问题描述】:

我在杂项上包含了一些相关内容。通过在<body> 标记的末尾附近添加一个<script> 标记来加载网页,然后加载其他javascript 文件。流程有点复杂,所以我会在提问之前尝试解释一下:

  • 浏览器加载页面时,我们的 <script> 元素位于 <body> 元素的末尾附近
  • 脚本标签的 src 属性指向一个 javascript 文件,该文件(在某些情况下)注入第二个 <script> 元素
  • 注入的<script> 元素的src 属性指向另一个javascript 文件,该文件最终在页面的适当部分注入一些内容。

我们使用这种两阶段方法能够在决定是否包含最终内容之前进行一些基本处理,这可能需要一些时间来加载。

问题在于 IE8(可能还有旧版本)两次加载最后一个 javascript。设置 src 属性的行为似乎会触发加载,但将脚本标记附加到 DOM 也会如此。有什么办法可以避免吗?

我已经创建了一个bare-bones demo 的问题。如果你有一些跟踪 HTTP 请求的方法,你会看到 IE8 加载了 js_test2.js 两次。

【问题讨论】:

    标签: javascript jquery internet-explorer


    【解决方案1】:

    根本区别在于,IE第一次将脚本元素添加到父元素的childNodes时会执行该脚本元素,而不管父元素是否实际在文档中。其他浏览器仅在将脚本添加到文档的 childNodes 树中的节点时才执行脚本。

    jQuery 的 domManip 函数(jQuery 1.3.2 的第 524 行)由 append 和其他类似的 jQuery 方法调用,它试图变得聪明,并为它在最终解析的 HTML,以执行脚本(如果需要,通过对外部脚本执行 AJAX 请求)。 (实际的脚本元素已从解析的子节点中删除,以阻止它们在插入文档时执行,大概是这样,当包含它们的内容一次附加到多个元素时,脚本只会执行一次。)

    但是,由于之前的clean 函数在解析 HTML 时将脚本元素附加到包装 div 中,因此 IE 已经执行了该脚本。所以你得到了两次处决。

    当您对脚本执行任何操作时,最好避免将domManip 之类的append 函数与HTML 字符串一起使用。

    事实上,忘记将您的内容放入序列化的 HTML 字符串并让 jQuery 解析它;只需使用更可靠的普通 DOM 方式即可:

    var s= document.createElement('script');
    s.type= 'text/javascript';
    s.charset= 'UTF-8';
    s.src= 'js_test2.js';
    document.getElementById('some_container').appendChild(s);
    

    (老实说,在查看了令人反胃的 clean 函数的源代码后,我对使用 jQuery 的基于 HTML 字符串的 DOM 操作进行任何操作都产生了严重的怀疑。它应该修复浏览器错误,但在我看来,哑正则表达式处理可能会导致尽可能多的问题。)

    顺便说一下这个初始调用:

    document.write(unescape("%3Cscript src='js_test1.js' type='text/javascript'%3E%3C/script%3E"));
    

    你不需要在这里转义;我不知道为什么这么多人这样做。在内联脚本中需要避免序列</,因为它会结束<script>标签,如果你正在做XHTML,<&也应该避免(或者]]>,如果你是使用 CDATA 包装器),但有一种更简单的方法可以只使用 JavaScript 字符串文字:

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

    【讨论】:

    • 感谢您提供如此详尽的回答。我确实已经恢复到更传统的 DOM 方式,并且现在运行良好。关于初始调用中的转义:如果我没记错的话,我复制了 Google Analytics 用来包含他们的脚本的代码,假设(可能是错误的)如果有人知道如何稳健地包含远程 javascript,那就是 GA 人。感谢您的澄清。
    【解决方案2】:

    我已经看到在类似情况下(例如克隆<script> 节点)在其他版本的 IE 上发生这种行为,我从来不知道如何阻止脚本执行两次,所以我最终做的是在脚本代码上添加一个守卫,使其不会运行两次。是这样的:

    if (typeof(loaded) == 'undefined') {
        // the whole code goes in here
        var loaded = true;
    }
    

    如果您找不到阻止脚本执行两次的方法,您可能想尝试该方法。

    【讨论】:

    • awesome,试图找到一种方法来检查是否定义了全局变量,当然没有在脚本运行之前将其定义为 false。谢谢!
    【解决方案3】:

    看起来 jquery 正在使用 XmlHttpRequest 获取 js_test2 的第二个实例。

    为什么我不知道,但您需要调查它的 JQuery 行为。

    【讨论】:

    • 这很奇怪,但看起来你是对的。如果我使用 appendChild 而不是 jQuery 的 append 它不会加载 js_test2.js 两次。我想如果我制作了演示/really/bare-bones(没有jQuery),我自己会发现这个。谢谢!
    • 我做了一些调查。结果(下)不好。
    【解决方案4】:

    您可以在加载之前检查脚本是否已经在文档中-

    function fetchscript(url, callback){
     var S= document.getElementsByTagName('script'), L= S.length;
     while(L){
      if(S[--L].src== url) return true;
     }
     //create and append script and any callbacks
    }
    

    【讨论】:

      【解决方案5】:

      我也发现了同样的问题,只出现在 IE 中。

      jQuery的html()方法链是:html>append>domManip>clean

      clean()方法使用innerHTML将字符串转成DOM,IE中的innerHTML有个bug,script标签会立即加载(第一次加载),jQuery evalScript脚本标签在domManip末尾method(ajax load).then 脚本文件在 IE 中加载两次。

      我认为 jQuery 应该解决这个问题,更新 clean() 方法

      【讨论】:

        【解决方案6】:

        这可能是因为当您已经在 <script> 元素中时使用了 document.write。您是否尝试过附加到 <head> 而不是 document.write

        【讨论】:

          猜你喜欢
          • 2019-05-24
          • 1970-01-01
          • 2012-01-19
          • 1970-01-01
          • 1970-01-01
          • 2012-05-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多