72

让我们面对现实吧,jQuery/jQuery-ui 下载量很大。

Google 建议延迟加载 JavaScript以加快初始渲染速度。我的页面使用 jQuery 来设置一些位于页面下方的选项卡(主要是在初始视图之外),我想将 jQuery 推迟到页面呈现之后。

谷歌的延迟代码在页面加载后通过钩子到正文 onLoad 事件向 DOM 添加一个标签:

<script type="text/javascript">

 // Add a script element as a child of the body
 function downloadJSAtOnload() {
 var element = document.createElement("script");
 element.src = "deferredfunctions.js";
 document.body.appendChild(element);
 }

 // Check for browser support of event handling capability
 if (window.addEventListener)
 window.addEventListener("load", downloadJSAtOnload, false);
 else if (window.attachEvent)
 window.attachEvent("onload", downloadJSAtOnload);
 else window.onload = downloadJSAtOnload;

</script>

我想以这种方式推迟 jQuery 的加载,但是当我尝试它时,我的 jQuery 代码找不到 jQuery(我并不完全出乎意料):

$(document).ready(function() {
    $("#tabs").tabs();
});

所以,似乎我需要找到一种方法来推迟我的 jQuery 代码的执行,直到 jQuery 被加载。如何检测添加的标签已完成加载和解析?

作为推论,异步加载似乎也包含一个答案。

有什么想法吗?

4

17 回答 17

54

试试这个,这是我不久前从 jQuerify 小书签中编辑的。我经常使用它来加载 jQuery 并在加载后执行一些东西。您当然可以将那里的 url 替换为您自己的 url 到您的自定义 jquery。

(function() {
      function getScript(url,success){
        var script=document.createElement('script');
        script.src=url;
        var head=document.getElementsByTagName('head')[0],
            done=false;
        script.onload=script.onreadystatechange = function(){
          if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
            done=true;
            success();
            script.onload = script.onreadystatechange = null;
            head.removeChild(script);
          }
        };
        head.appendChild(script);
      }
        getScript('http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js',function(){
            // YOUR CODE GOES HERE AND IS EXECUTED AFTER JQUERY LOADS
        });
    })();

我真的会将 jQuery 和 jQuery-UI 合并到一个文件中并使用一个 url。如果您真的想单独加载它们,只需链接 getScripts:

getScript('http://myurltojquery.js',function(){
        getScript('http://myurltojqueryUI.js',function(){
              //your tab code here
        })
});
于 2011-05-02T04:03:17.163 回答
14

由于这是一个重要主题的最高级别问题,因此让我如此大胆地根据@valmarv 和@amparsand 的先前回答提供我自己的看法。

我正在使用多维数组来加载脚本。将它们之间没有依赖关系的那些组合在一起:

var dfLoadStatus = 0;
var dfLoadFiles = [
      ["http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"],
      ["http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js",
       "/js/somespecial.js",
       "/js/feedback-widget.js#2312195",
       "/js/nohover.js"]
     ];

function downloadJSAtOnload() {
    if (!dfLoadFiles.length) return;

    var dfGroup = dfLoadFiles.shift();
    dfLoadStatus = 0;

    for(var i = 0; i<dfGroup.length; i++) {
        dfLoadStatus++;
        var element = document.createElement('script');
        element.src = dfGroup[i];
        element.onload = element.onreadystatechange = function() {
        if ( ! this.readyState || 
               this.readyState == 'complete') {
            dfLoadStatus--;
            if (dfLoadStatus==0) downloadJSAtOnload();
        }
    };
    document.body.appendChild(element);
  }

}

if (window.addEventListener)
    window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
    window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;

它在加载后加载第一个 jquery,它会立即继续加载其他脚本。您可以通过添加到页面上任何位置的数组来轻松添加脚本:

dfLoadFiles.push(["/js/loadbeforeA.js"]);
dfLoadFiles.push(["/js/javascriptA.js", "/js/javascriptB.js"]);
dfLoadFiles.push(["/js/loadafterB.js"]);
于 2013-08-23T14:34:17.070 回答
10

这是对异步/延迟 javascript 加载的现代方法的一个很好的描述。但它不适用于内联脚本

<script type="text/javascript" src="/jquery/3.1.1-1/jquery.min.js" defer></script>
<script type="text/javascript" defer>
    $(function () {   //  <- jquery is not yet initialized
      ...
    });
</script>

@nilskp 建议了最简单的异步加载解决方案 - externalize script:

<script type="text/javascript" src="/jquery/3.1.1-1/jquery.min.js" defer></script>
<script type="text/javascript" src="resources/js/onload.js" defer></script>
于 2017-01-30T23:37:59.770 回答
3
element.addEventListener("load", function () {
    $('#tabs').tabs()
}, false);

试试看。

于 2011-05-02T03:21:24.890 回答
3

我在 async/defered jquery 脚本标签之后添加了这段代码,这定义了一个临时函数 $ ,它将累积在所有内容加载完成时需要运行的任何内容,然后一旦我们完成,此时使用 $将被覆盖以执行功能。使用这段代码,无需在文档中进一步更改 jQuery onload 语法。

<script defer async src="https://code.jquery.com/jquery-2.2.0.min.js">
<script>
    var executeLater = [];
    function $(func) {
        executeLater.push(func);
    }
    window.addEventListener('load', function () {
        $(function () {
            for (var c = 0; c < executeLater.length; c++) {
                executeLater[c]();
            }
        });
    })
</script>

....进而...

<script>
    $(function() {
        alert("loaded");
    });
</script>
于 2016-01-14T12:49:34.500 回答
2

将 jQuery 和依赖于 jQuery 的代码放在 HTML 文件的末尾。

编辑:更清楚一点

<html>
<head></head>
<body>
    <!-- Your normal content here -->
    <script type="text/javascript" src="http://path/to/jquery/jquery.min.js"></script>
    <script>//Put your jQuery code here</script>
</body>
</html>
于 2011-05-02T01:39:34.897 回答
2

在某些情况下,您可以在加载 jquery 时触发事件。

<script type="text/javascript">
    (function (window) {

        window.jQueryHasLoaded = false;

        document.body.addEventListener('jqueryloaded', function (e) {
            console.log('jqueryloaded ' + new Date() );
        }, false);

        function appendScript(script) {
            var tagS = document.createElement("script"), 
                s = document.getElementsByTagName("script")[0];
            tagS.src = script.src;
            s.parentNode.insertBefore(tagS, s);

            if ( script.id == 'jquery' ) {
                tagS.addEventListener('load', function (e) {
                    window.jQueryHasLoaded = true;
                    var jQueryLoaded = new Event('jqueryloaded');
                    document.body.dispatchEvent(jQueryLoaded);
                }, false);
            }
        }

        var scripts = [
            {
                'id': 'jquery',
                'src': 'js/libs/jquery/jquery-2.0.3.min.js'
            },
            {
                'src': 'js/myscript1.js'
            },
            {
                'src': 'js/myscript2.js'
            }
        ];

        for (var i=0; i < scripts.length; i++) {
            appendScript(scripts[i]);
        }

    }(window));
</script>

然后将您的依赖项包装在一个函数中:

// myscript1.js 
(function(){ 

    function initMyjQueryDependency() {
        console.log('my code is executed after jquery is loaded!');
        // here my code that depends on jquery
    }

    if ( jQueryHasLoaded === true )
        initMyjQueryDependency();
    else
        document.body.addEventListener('jqueryloaded', initMyjQueryDependency, false);

}());

如果 jquery 在其他脚本之后完成加载,则在触发 jqueryloaded 事件时将执行您的依赖项。

如果 jquery 已经加载,jQueryHasLoaded === true你的依赖将被执行initMyjQueryDependency()

于 2013-11-19T11:43:13.680 回答
1

以下代码应在窗口加载完成后加载您的脚本:

<html>
<head>
    <script>
    var jQueryLoaded = false;
    function test() {
        var myScript = document.createElement('script');
        myScript.type = 'text/javascript';
        myScript.async = true;
        myScript.src = jQueryLoaded ? 'http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.js' : 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js';
        document.body.appendChild(myScript);

        if(!jQueryLoaded){
            alert('jquery was loaded');
            jQueryLoaded = true;
            test();
        } else {
            alert('jqueryui was loaded');   
        }
    }

    if (window.addEventListener){
        alert('window.addEventListener');
        window.addEventListener("load", test, false);
    } else if (window.attachEvent){
        alert('window.attachEvent');
        window.attachEvent("onload", test);
    } else{
        alert('window.onload');
        window.onload = test;
    }
    </script>
</head>
<body>
<p>Placeholder text goes here</p>
</body>
</html>

在 Chrome、FF 和 IE9 中为我工作 - 让我知道这是否有帮助

于 2011-05-02T01:47:12.387 回答
1

好吧,在我看来,您所要做的就是 a) 将要在加载时运行的 jQuery 代码添加到 jQuery 文件的末尾,或者 b) 将其附加到downloadJSAtOnload函数中,如下所示:

<script type="text/javascript">

 // Add a script element as a child of the body
 function downloadJSAtOnload() {
 var element = document.createElement("script");
 element.src = "deferredfunctions.js";
 document.body.appendChild(element);
 $("#tabs").tabs(); // <==== NOTE THIS. This should theoretically run after the
                    // script has been appended, though you'll have to test this
                    // because I don't know if the JavaScript above will wait for
                    // the script to load before continuing
 }

 // Check for browser support of event handling capability
 if (window.addEventListener)
 window.addEventListener("load", downloadJSAtOnload, false);
 else if (window.attachEvent)
 window.attachEvent("onload", downloadJSAtOnload);
 else window.onload = downloadJSAtOnload;

</script>
于 2011-05-02T02:32:27.813 回答
1

这是我的版本,它支持链接以确保脚本一个接一个地加载,基于&的代码:

var deferredJSFiles = ['jquery/jquery', 'file1', 'file2', 'file3'];
function downloadJSAtOnload() {
    if (!deferredJSFiles.length)
        return;
    var deferredJSFile = deferredJSFiles.shift();
    var element = document.createElement('script');
    element.src = deferredJSFile.indexOf('http') == 0 ? deferredJSFile : '/js/' + deferredJSFile + '.js';
    element.onload = element.onreadystatechange = function() {
        if (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')
            downloadJSAtOnload();
    };
    document.body.appendChild(element);
}
if (window.addEventListener)
    window.addEventListener('load', downloadJSAtOnload, false);
else if (window.attachEvent)
    window.attachEvent('onload', downloadJSAtOnload);
else
    window.onload = downloadJSAtOnload;
于 2012-09-25T08:57:40.863 回答
1
<!doctype html>
<html>
    <head>

    </head>
    <body>
        <p>If you click on the "Hide" button, I will disappear.</p>
        <button id="hide" >Hide</button>
        <button id="show" >Show</button>

        <script type="text/javascript">
            function loadScript(url, callback) {

                var script = document.createElement("script")
                script.type = "text/javascript";

                if (script.readyState) {  //IE
                    script.onreadystatechange = function() {
                        if (script.readyState == "loaded" ||
                                script.readyState == "complete") {
                            script.onreadystatechange = null;
                            callback();
                        }
                    };
                } else {  //Others
                    script.onload = function() {
                        callback();
                    };
                }

                script.src = url;
                document.body.appendChild(script);
            }
            loadScript("http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js",
                    function() {
                        //YAHOO.namespace("mystuff");
                        $("#show").click(function() {
                            $("p").show();
                        });
                        $("#hide").click(function() {
                            $("p").hide();
                        });

                        //more...
                    });
        </script>

    </body>
</html>
于 2013-10-15T07:24:56.120 回答
1

我认为 Modernizr.load() 在这里值得一提 - 它很好地处理依赖项加载

于 2014-12-17T23:34:18.133 回答
1

Google Analytics 使用了一个简单的技巧。

准备

1.在 HTML 的头部添加一个小脚本

<script>
    window.jQuery_store = [];
    window.jQueryReady = function (fn) { jQuery_store.push(fn); }
</script>

jQueryReady 函数只会将委托保存到一个数组中以供将来使用。

2.创建一个新的 JS 脚本jquery-ready.js,包含下一个内容:

// When jQuery is finaly ready
$(function() {
    // Replace previous implementation
    window.jQueryReady = function(fn) {
        // jQuery is loaded call immediately
        fn();
    }
    
    // Call all saved callbacks
    for (var i = 0; i < window.jQuery_store.length; ++i) {
        try {
            window.jQuery_store[i]();
        } catch (err) {
            console.log(err);
        }
    }
})

加载此脚本后,它将:

  • 等到jQuery可以安全使用
  • jQueryReady用一个新函数替换该函数,该函数只立即调用委托(jQuery 在给定时间准备就绪)。
  • 遍历从先前jQueryReady调用中保存的函数。

3.把所有东西放在一起让 jquery-ready.js 只在 jQuery 加载后加载。在您的页脚中,您将拥有:

<script defer src=".../jquery.min.js">
<script defer src=".../bootstrap.min.js">
... any other plugins for jQuery you probably need
<script defer src=".../jquery-ready.js">

这确保 jquery-ready.js 脚本仅在 jQuery 执行后才会执行。

用法

您现在可以随时使用 jQueryReady 函数。

jQueryReady(function() {
    // jQuery is safe to use here
    $("div.to-be-hidden").hide();
})
于 2020-10-16T00:44:55.840 回答
0

看来您只需要<script defer>http ://www.w3schools.com/tags/att_script_defer.asp

于 2011-05-02T03:10:24.517 回答
0

看一看jQuery.holdReady()

“保持或释放 jQuery 的就绪事件的执行。” (jQuery 1.6+)

http://api.jquery.com/jQuery.holdReady/

于 2011-05-29T19:55:31.067 回答
0

我想推迟在 WordPress 网站上加载 jQuery,所以我实际上无法使用它更新所有引用。而是编写了一个小包装器来对 jQuery 调用进行排队,然后在它最终加载时调用。把它放在头上,只有 250 字节左右,这意味着 jQuery 可以被延迟或异步加载,而不会改变所有现有的引用。让我的加载时间更好。

我的快速代码可能不适用于所有 jQuery 函数,但到目前为止,除了我尝试过的一个函数调用之外,它已经适用于所有函数。通常我会做这样的自定义东西,我不会让它可用,但我想我会把这个片段放到网上。可在此处获取https://github.com/andrewtranter/jQuery_deferred_compat

于 2021-12-01T06:29:26.067 回答
-2

使用http://labjs.com在 html 末尾加载所有脚本,这是 100% 的解决方案,我根据 gtmetrix 规则对其进行了多次测试。示例http://gtmetrix.com/reports/interactio.cz/jxomHSLV

于 2014-02-07T10:10:37.953 回答