【问题标题】:Handling code which relies on jQuery before jQuery is loaded在加载 jQuery 之前处理依赖 jQuery 的代码
【发布时间】:2011-11-28 15:50:41
【问题描述】:

我想遵循将所有 JavaScript 放在页面最底部的一般准则,以加快加载时间,并解决一些与 Web 应用程序 (Django) 中的 jQuery 版本冲突的麻烦问题。

但是,我经常有一些代码,这些代码依赖于 jQuery,但必须在页面上更靠前(基本上不能将代码移到底部)。

我想知道是否有一种简单的方法来编写代码,这样即使尚未定义 jQuery,当定义 jQuery 时代码也可以工作。

我不得不说,以下似乎有点矫枉过正,但我​​不知道另一种方法:

function run_my_code($) {
    // jquery-dependent code here
    $("#foo").data('bar', true);
}
var t = null;
function jquery_ready() {
    if (window.jQuery && window.jQuery.ui) {
        run_my_code(window.jQuery);
    } else {
        t = window.setTimeout(jquery_ready, 100);
    }
}
t = window.setTimeout(jquery_ready, 100);

实际上,我可能需要在一个页面中多次使用代码,这些代码不了解其他代码,因此除非我将每个 jquery_ready 重命名为 jquery_ready_guid、jquery_ready_otherguid 等等。

澄清

这很清楚,我将包含到 JavaScript (<script type="text/javascript" src="jquery.min.js" />) 放在页面的最底部,就在 </body> 之前。所以我不能使用$

【问题讨论】:

    标签: javascript jquery


    【解决方案1】:

    简单使用纯javascript版本的$(document).ready();

    document.addEventListener("DOMContentLoaded", function(event) { 
        //you can use jQuery there
    });
    

    【讨论】:

    • 这就是我想要的。我更喜欢 Vanilla,但当代码不是你的时,你必须适应。
    【解决方案2】:

    您的方式是我所知道的唯一方式,但我会确保范围更窄一些:

    (function() {
      var runMyCode = function($) {
        // jquery-dependent code here
        $("#foo").data('bar', true);
      };
    
      var timer = function() {
        if (window.jQuery && window.jQuery.ui) {
          runMyCode(window.jQuery);
        } else {
          window.setTimeout(timer, 100);
        }
      };
      timer();
    })();
    

    更新

    这是我拼凑起来的一个小延迟加载器:

    var Namespace = Namespace || { };
    Namespace.Deferred = function () {
      var functions = [];
      var timer = function() {
        if (window.jQuery && window.jQuery.ui) {
            while (functions.length) {
                functions.shift()(window.jQuery);
            }
        } else {
            window.setTimeout(timer, 250);
        }
      };
      timer();
      return {
        execute: function(onJQueryReady) {
            if (window.jQuery && window.jQuery.ui) {
                onJQueryReady(window.jQuery);
            } else {
                functions.push(onJQueryReady);
            }
        }
      };
    }();
    

    然后可以像这样使用:

    Namespace.Deferred.execute(runMyCode);
    

    【讨论】:

    • 不应该是typeof(jQuery) !== "undefined" 而不是window.jQuery
    • @BenLeggiero 在实践中,我的答案中发布的内容和您的 sn-p 将是相同的,(作为个人喜好问题)我喜欢在编写代码时使用 JS 的真假性质。
    【解决方案3】:

    我发现的最好的方法是把代码写在一个函数中,并在jquery被加载后调用该函数:

    function RunAfterjQ(){
    // Codes that uses jQuery
    }
    
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
    <script type="text/javascript">
        RunAfterjQ();
    </script>
    

    更新:对于母版页,您可以定义一个数组以在母版页头部推送函数:

    var afterJQ = [];
    

    然后在母版页底部运行推入该数组的所有函数:

    <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
    <script type="text/javascript">
        for(var i = 0; i < afterJQ.length; i++) afterJQ[i]();
    </script>
    

    在任何需要使用依赖于 jQuery 的 javascript 并且在定义 jQuery 之前的地方,只需将它推入这个数组即可:

    afterJQ.push( function() { 
        // this code will execute after jQuery is loaded.
     });
    

    【讨论】:

    • 很好的答案!最简单、最快捷且完美运行!
    【解决方案4】:

    这是一种编写注入代码的方法,该代码仅在 jQuery 加载后运行(无论是同步还是异步)。

    <script>
    if ( ! window.deferAfterjQueryLoaded ) {
        window.deferAfterjQueryLoaded = [];
        Object.defineProperty(window, "$", {
            set: function(value) {
                window.setTimeout(function() {
                    $.each(window.deferAfterjQueryLoaded, function(index, fn) {
                        fn();
                    });
                }, 0);
                Object.defineProperty(window, "$", { value: value });
            },
    
            configurable: true
        });
    }
    
    window.deferAfterjQueryLoaded.push(function() {
        //... some code that needs to be run
    });
    </script>
    

    这是做什么的:

    1. 延迟定义deferAfterjQueryLoaded,因此您无需将其注入head
    2. window.$ 定义setter。当 jQuery 加载时,它所做的最后一件事就是分配给全局 $ 变量。这允许您在发生这种情况时触发函数。
    3. 安排延迟函数在 jQuery 脚本完成后尽快运行 (setTimeout(..., 0);)。
    4. setter 是否自行移除。

    为了完全清洁,您也可以将预定功能删除 deferAfterjQueryLoaded

    【讨论】:

    • 糟糕的解决方案。当上面定义了 jQuery 时,它不会检查属性是否已经定义并覆盖它,从而使 jQuery 损坏。代码应该检查 jquery 是否到位,在这种情况下不要重新定义属性,而是立即安排函数执行。
    • @mdzieg 问题明确指出 jQuery 的 &lt;script&gt; 标记将是页面上的最后一条语句。请参阅问题底部的“澄清”。
    【解决方案5】:

    怎么样:

    <script>
        window.deferAfterjQueryLoaded = [];
        window.deferAfterjQueryLoaded.push(function() {
            //... some code that needs to be run
        });
    
        // ... further down in the page
    
        window.deferAfterjQueryLoaded.push(function() {
            //... some other code to run
        });
    </script>
    
    <script src="jquery.js" />
    <script>
        $.each(window.deferAfterjQueryLoaded, function(index, fn) {
            fn();
        });
    </script>
    

    这是可行的,因为这里的每个脚本都是完全阻塞的。这意味着deferAfterjQueryLoaded 数组的创建以及所有正在创建并推送到该数组的函数首先发生。然后 jQuery 完全加载。然后遍历该数组并执行每个函数。如果脚本位于不同的文件中,则此方法同样有效。

    如果您还希望 DOMReady 触发,您可以在其中一个 deferAfterjQueryLoaded 函数中嵌套 $(function() {}),如下所示:

    window.deferAfterjQueryLoaded.push(function() {
        $(function() {
            console.log('jquery loaded and the DOM is ready');
        });
        console.log('jquery loaded');
    });
    

    最终,您应该真正重构您的代码,以便所有内容实际上都在底部,并拥有一个有利于该模型的系统。它更容易理解发生的所有事情并且性能更高(尤其是如果您有单独的脚本)。

    【讨论】:

    • 这确实与其他一些方法没有什么不同,但无论如何感谢您的回答。我遇到的问题是我需要将一些依赖于 jQuery 的代码插入到我无法真正控制的页面上。也就是说,我可以添加一些 JavaScript,但无法控制它的放置位置。更糟糕的是,jQuery 是异步加载的,所以我不能简单地使用window.onload。我现在要做的是等待window.onload 并每隔 300 毫秒左右检查一次 jQuery 对象......我想删除一个 hack。如果它没有内置到 jQuery 中,也许这是唯一的方法。
    【解决方案6】:

    我也有同样的问题,但是对于大量文件,我使用headjs 来管理加载,它只有 2kb,所以无论如何,对我来说,放入标题并没有真正的问题。然后你的代码就变成了,

    head.ready(function(){
      $...
    });
    

    在页面底部,

    head.js('/jquery.min.js');
    

    【讨论】:

    • 嗯...这可能有效。不过,引入另一个 JS 依赖项并不疯狂。
    【解决方案7】:

    您应该能够在 document ready 事件上执行此操作。

    【讨论】:

    • 类似document.addEventListener("load", function () { run_my_code(window.jQuery) });?
    • 似乎问题在于您只能附加一次加载功能,因此我只能处理每页一个实例。可能不止一个。
    • 那么为什么文档会这样说:“如果在同一个 EventTarget 上使用相同的参数注册了多个相同的 EventListener,则会丢弃重复的实例。它们不会导致 EventListener 被调用两次,并且由于重复项被丢弃,因此不需要使用 removeEventListener 方法手动删除它们。” developer.mozilla.org/en/DOM/…
    • 第二个参数不同。
    • 使用 on ready 事件的问题是 $ 仍然没有映射到命名空间中。在 Chrome 中出现 JS 错误。
    【解决方案8】:
    function jQueryGodot(code)
    {
        if (window.jQuery)
        {
            code(window.jQuery);        
        }
        else
        {
            if (!window.$)
            {
                window.$ = { codes: [] };
                window.watch('$', function(p, defered, jQuery) {
                    jQuery.each(defered.codes, function(i, code) {
                        code(jQuery);
                    });
                    return jQuery;
                });
            }
    
            window.$.codes.push(code);
        }
    }
    
    jQueryGodot(function($) {
        $('div').html('Will always work!');
    })
    

    Working example on JSFiddle.

    传递给 jQueryGodot 函数的代码无论是在 jQuery 加载之前还是之后调用,都会一直执行。

    该解决方案依赖于 Object.watch,它在大多数浏览器中都需要这种 polyfill(缩小 660 字节):https://gist.github.com/adriengibrat/b0ee333dc1b058a22b66

    【讨论】:

    【解决方案9】:

    您可以推迟所有调用,例如 jQuery(function(){...}) 没有 setTimeout 循环:https://jsfiddle.net/rL1f451q/3/

    它将每个 jQuery(...) 调用收集到一个数组中,直到没有定义 jQuery,然后第二个代码在 jQuery 可用时执行它们。

    把这个放在头部或正文的开头:

    <!-- jQuery defer code body: deferring jQuery calls until jQuery is loaded -->
    <script>
      window.jQueryQ = window.jQueryQ || [];
      window.$ = window.jQuery = function(){
        window.jQueryQ.push(arguments);
      }
    </script>
    <!-- end: jQuery defer code body -->
    

    这是在正文的最后, jQuery 脚本之后:

    <!-- jQuery deferring code footer: add this to the end of body and after the jQuery code -->
    <script>
      jQuery(function(){
        jQuery.each(window.jQueryQ||[],function(i,a){
          // to understand why setTimeout 0 is useful, see: https://www.youtube.com/watch?v=8aGhZQkoFbQ, tldr: having a lot of calls wont freeze the website
          setTimeout(function(){
            jQuery.apply(this,a);
          },0);
        });
      });
    </script>
    <!-- end: jQuery deferring code footer -->
    

    【讨论】:

      【解决方案10】:

      我写了简单的 js 代码来处理这种情况:https://github.com/Yorkii/wait-for

      那你就可以这样使用了

      waitFor('jQuery', function () {
         //jQuery is loaded
         jQuery('body').addClass('done');
      });
      

      您甚至可以等待多个库

      waitFor(['jQuery', 'MyAppClass'], function () {
         //Both libs are loaded
      });
      

      【讨论】:

        【解决方案11】:

        我正在使用 JavaScript 承诺发布我的答案。它有效,简单且可重复使用。

        优点是,无论页面多早或多晚,只要 jQuery 加载,代码就会立即执行。

        但是,到目前为止,我不像这个帖子中的其他人那样有经验。因此,我希望我的答案能够得到同行评审。我想知道它是否真的是一个有效的解决方案以及缺点是什么。

        编写一个objectExists 函数,并在代码中随心所欲地编写一个承诺。

        function jqueryExists() {
            return new Promise(function (resolve, reject) {
                (function waitForJquery() {
                    if (jQuery) return resolve();
                    setTimeout(waitForJquery, 30);
                })();
            });
        }
        

        然后添加依赖于 jQuery 的函数。据我了解,它不会阻止任何其他脚本的执行,即使稍后加载 jQuery,页面也会正常加载。

        jqueryExists().then(function(){
            // Write your function here
        }).catch(function(){
            console.log('jQuery couldn\'t be loaded');
        })
        

        现在可以在这段代码之后加载 jQuery,并且一旦 jQuery 可用,该函数就会执行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-21
          • 1970-01-01
          • 2017-11-16
          • 2020-02-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多