Handling code which relies on jQuery before jQuery is loaded

前端 未结 10 997
挽巷
挽巷 2020-12-08 06:31

I\'d like to follow the general guideline of putting all JavaScript at the very bottom of the page, to speed up loading time and also to take care of some pesky issues with

相关标签:
10条回答
  • 2020-12-08 07:27

    You can defer all calls like jQuery(function(){...}) without the loop of a setTimeout: https://jsfiddle.net/rL1f451q/3/

    It is collecting every jQuery(...) call into an array until jQuery is not defined, then the second code executes them when jQuery is available.

    Put this in the head or the beginning of the body:

    <!-- 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 -->
    

    And this at the very end of the body, after the jQuery script:

    <!-- 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 -->
    
    0 讨论(0)
  • Here is a way to write injected code that will be run only after jQuery loads (whether synchronously or asynchronously).

    <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>
    

    What this does is:

    1. Defines deferAfterjQueryLoaded lazily, so you don't need to inject that into head.
    2. Defines a setter for window.$. When jQuery loads, one of the last things it does is assign to the global $ variable. This allows you to trigger a function when that happens.
    3. Schedules the deferred functions to run as soon as possible after the jQuery script finishes (setTimeout(..., 0);).
    4. Has the setter remove itself.

    For complete cleanliness you could have the scheduled function remove deferAfterjQueryLoaded as well.

    0 讨论(0)
  • 2020-12-08 07:35

    Your way is the only way that I know of, though I would ensure that the scoping is a little tighter:

    (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();
    })();
    

    Update

    Here's a little deferred loader I cobbled together:

    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);
            }
        }
      };
    }();
    

    Which would then be useable like so:

    Namespace.Deferred.execute(runMyCode);
    
    0 讨论(0)
  • 2020-12-08 07:35

    You should be able to do this on a document ready event.

    0 讨论(0)
提交回复
热议问题