如何使脚本执行等到加载jQuery


106

我遇到了页面加载速度如此之快的问题,以至于jquery在后续脚本调用之前尚未完成加载。有没有一种方法来检查是否存在jquery,如果不存在,请稍等片刻然后重试?


针对以下答案/评论,我将发布一些标记。

情况... asp.net母版页和子页。

在母版页中,我引用了jquery。然后,在内容页面中,我引用了特定于页面的脚本。加载页面特定的脚本时,它抱怨“ $未定义”。

我在标记的几个点上放置了警报,以查看事物触发的顺序,并确认它按以下顺序触发:

  1. 母版页标题。
  2. 子页面内容块1(位于母版页头内部,但在调用母版页脚本之后)。
  3. 子页面内容块2。

这是母版页顶部的标记:

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="SiteMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Reporting Portal</title>
    <link href="~/Styles/site.css" rel="stylesheet" type="text/css" />
    <link href="~/Styles/red/red.css" rel="stylesheet" type="text/css" />
    <script type="text/Scripts" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> 
    <script type="text/Scripts" language="javascript" src="../Scripts/jquery.dropdownPlain.js"></script>
    <script type="text/Scripts" language="javascript" src="../Scripts/facebox.js"></script>
    <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
</head>

然后,在母版页的正文中,还有一个附加的ContentPlaceHolder:

 <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
                </asp:ContentPlaceHolder>

在子页面中,如下所示:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Dashboard.aspx.cs" Inherits="Data.Dashboard" %>
<%@ Register src="../userControls/ucDropdownMenu.ascx" tagname="ucDropdownMenu" tagprefix="uc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
    <link rel="stylesheet" type="text/css" href="../Styles/paserMap.css" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
***CONTENT HERE***
    <script src="../Scripts/Dashboard.js" type="text/javascript"></script>
</asp:Content>

这是“ ../Script/Dashboard.js”文件的内容:

    $(document).ready(function () {

    $('.tgl:first').show(); // Show the first div

    //Description: East panel parent tab navigation
    $('.tabNav label').click(function () {
        $('.tabNav li').removeClass('active')
        $(this).parent().addClass('active');

        var index = $(this).parent('li').index();
        var divToggle = $('.ui-layout-content').children('div.tgl');

        //hide all subToggle divs
        divToggle.hide();
        divToggle.eq(index).show();
    });

});

4
你在用$(document).ready(function(){...});吗?
rownage 2011年

如果使用简单<script src="...">标签加载脚本,则不会发生这种情况。您是否正在使用RequireJS或LABjs之类的东西?
Pointy

您是否在$(document).ready()或中运行后续脚本$(window).load()?我们可以看个例子吗?
John Hartsock 2011年

1
@AmandaMyer现在,请忽略所有有关使用文档准备就绪的建议,因为您已经在这样做了,但是我敢打赌,正是mimetype导致它们无法同步加载
Sander

2
尝试更改type="text/Scripts"type="text/javascript",或者将其不再使用不再需要,也将其删除language="javascript。最好开始尝试。
杰克

Answers:


11

编辑

您能为脚本标签尝试正确的类型吗?我看到您使用text/Scripts,这不是javascript的正确模仿类型。

用这个:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> 
<script type="text/javascript" src="../Scripts/jquery.dropdownPlain.js"></script>
<script type="text/javascript" src="../Scripts/facebox.js"></script>

结束编辑

或者您可以查看require.js,它是您的JavaScript代码的加载程序。

根据您的项目,这可能有点过大


205

晚会晚了,类似于Briguy37的问题,但是为了以后参考,我使用以下方法并将要推迟到加载jQuery的函数传递给我:

function defer(method) {
    if (window.jQuery) {
        method();
    } else {
        setTimeout(function() { defer(method) }, 50);
    }
}

它将每隔50ms递归调用defer方法,直到window.jQuery存在并退出并调用method()

具有匿名功能的示例:

defer(function () {
    alert("jQuery is now loaded");
});

2
这对我没有用。在方法内部,未定义$。
亚伦利芬

这是使某些自定义代码在Adobe Muse中其他人设计的网站上工作的不错的解决方案。
Buggabill '16

@gidim不会,因为它是计时器而不是循环。但是只要用户停留在页面上,它就会一直运行。
Micros

最好调查脚本的加载顺序。我发现,如果script加载jQuery 的标签具有defer属性,则尽管脚本定义了代码的顺序,但直到稍后才加载会导致问题。
ADTC '18年

19

您可以使用defer属性在实际末尾加载脚本。

<script type='text/javascript' src='myscript.js' defer='defer'></script>

但是通常以正确的顺序加载脚本应该可以解决问题,因此请确保将jquery include放在您自己的脚本之前

如果您的代码在页面中,而不是在单独的js文件中,那么您只需要在文档准备好并封装代码后执行脚本即可,如下所示:

$(function(){
//here goes your code
});

只要您有一个依赖项,就可以了
GuillaumeMassé2012年

17

尽管Darbio的defer方法更灵活,但还有另一种方法。

(function() {
  var nTimer = setInterval(function() {
    if (window.jQuery) {
      // Do something with jQuery
      clearInterval(nTimer);
    }
  }, 100);
})();

16

最简单,最安全的方法是使用以下代码:

var waitForJQuery = setInterval(function () {
    if (typeof $ != 'undefined') {

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);

2
天才解决方案。谢谢
Diego Fortes 18'Oct

13

您可以尝试onload事件。加载所有脚本后引发:

window.onload = function () {
   //jquery ready for use here
}

但是请记住,您可以覆盖使用window.onload的其他脚本。


这行得通,但是如果您的jquery改变了页面的外观,您可能会看到未样式化内容的闪烁
Matthew Lock

1
您可以添加事件侦听器,以避免覆盖其他脚本:stackoverflow.com/a/15564394/32453 FWIW”
rogerdpack

@rogerdpack同意,这是更好的解决方案。
Nigrimmist

10

我发现建议的解决方案仅在考虑异步代码时才有效。这是在两种情况下都可以使用的版本:

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.jQuery) return setTimeout(load, 50);
    //your synchronous or asynchronous jQuery-related code
}, false);

谢谢,仅在加载后检查一次,并且仅在回退开始时每隔50ms循环一次,这比仅让它与setInterval像最受好评的答案那样循环要好得多。在最佳情况下,您的延迟为0毫秒,而不是最高答案的平均情况为25毫秒。
卢克,

优美优雅-我喜欢您传递load函数的方式,而且还可以在中重用它setTimeout
Nemesarial

6

这是一个常见的问题,想象一下您使用了一个很酷的PHP模板引擎,因此您有了基本布局:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

当然,您可能会读到在页面底部加载Javascript更好的地方,因此您的动态内容不知道谁是jQuery(或$)。

另外,您在某处阅读了内联小型Java脚本是很好的,因此,假设您需要在页面baboom中使用jQuery,未定义$(.. yet ^^)。

我喜欢Facebook提供的解决方案

window.fbAsyncInit = function() { alert('FB is ready !'); }

因此,作为一个懒惰的程序员(我应该说一个好的程序员^^),您可以使用等效项(在您的页面内):

window.jqReady = function() {}

并在jQuery包含之后在布局的底部添加

if (window.hasOwnProperty('jqReady')) $(function() {window.jqReady();});

谢谢!我正面临这种情况。
KumZ '17

或将jquery负载移到顶部:)stackoverflow.com/a/11012073/32453
rogerdpack

5

除了“ wait”(通常使用来完成setTimeout)外,您还可以将窗口本身中的jQuery对象的定义用作执行依赖于它的代码的钩子。这可以通过使用定义的属性定义来实现Object.defineProperty

(function(){
  var _jQuery;
  Object.defineProperty(window, 'jQuery', {
    get: function() { return _jQuery; },
    set: function($) {
      _jQuery = $;

      // put code or call to function that uses jQuery here

    }
  });
})();

如果jQuery实际定义了window属性,这将非常有用,但是似乎不是吗?
吉姆(Jim

它实际上并没有设置属性,而是window.jQuery通过jQuery在全局范围内定义变量(即window)将其设置为值。这将触发代码中定义的窗口对象的属性设置器功能。
保护者一

如果在执行此代码之前定义了jQuery属性,则此方法将无效;即,如果您延迟的jquery.js在加载此代码之前加载。一种解决方案是将延迟的jquery.js放在之前</body>而不是之前</head>
user36388 '17

@user:只需在加载jQuery(延迟或其他方式)之前执行代码。加载位置无关紧要,只是我的代码片段在它之前。
保护者

1
如果您可以确保使用此脚本的脚本在将jQuery包含在文档中之前就非常有用。上面的循环方法都不是效率低下或竞赛案例。
RedYetiCo

4

用:

$(document).ready(function() {
    // put all your jQuery goodness in here.
});

查看此以获取更多信息:http : //www.learningjquery.com/2006/09/introducing-document-ready

注意:只要您的JQuery库的脚本导入在此调用之上,此方法就应该起作用。

更新:

如果由于某种原因您的代码未同步加载(我从未遇到过,但显然可能不会发生以下注释),则可以像下面这样编码。

function yourFunctionToRun(){
    //Your JQuery goodness here
}

function runYourFunctionWhenJQueryIsLoaded() {
    if (window.$){
        //possibly some other JQuery checks to make sure that everything is loaded here

        yourFunctionToRun();
    } else {
        setTimeout(runYourFunctionWhenJQueryIsLoaded, 50);
    }
}

runYourFunctionWhenJQueryIsLoaded();

6
这个等待直到dom准备就绪,直到加载jquery。他可能有2个脚本文件,其中1个来自CDN(来自Google的jquery,还有一个本地插件),其中1个文件比其他文件早加载...。如果插件的加载速度比jquery本身快,则您的代码无法解决此问题
Sander

2
我不同意@Sander,因为加载是同步的,所以应该可以工作
malko 2011年

您是对的,如果它们来自不同的域,那么我关于它们异步加载的假设是完全错误的!原谅我说
桑德

如果无法使用jquery,这将引发异常,从而使其永远不会进入看起来似乎为else的语句。猜猜它应该是一试通俗的东西。
山姆·斯托林加

@SamStoelinga:谢谢,现在应该解决此问题。
Briguy37 2012年

3

我不喜欢间隔事物。当我想推迟jquery或其他实际操作时,通常会发生如下情况。

从...开始:

<html>
 <head>
  <script>var $d=[];var $=(n)=>{$d.push(n)}</script>
 </head>

然后:

 <body>
  <div id="thediv"></div>

  <script>
    $(function(){
       $('#thediv').html('thecode');
    });
  </script>

  <script src="http://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>

然后最后:

  <script>for(var f in $d){$d[f]();}</script>
 </body>
<html>

或令人难以置信的版本:

<script>var def=[];function defer(n){def.push(n)}</script>
<script>
defer(function(){
   $('#thediv').html('thecode');
});
</script>
<script src="http://code.jquery.com/jquery-3.2.1.min.js" type="text/javascript"></script>
<script>for(var f in def){def[f]();}</script>

并且在异步的情况下,您可以在jQuery onload上执行推送的功能。

<script async onload="for(var f in def){def[f]();}" 
src="jquery.min.js" type="text/javascript"></script>

或者:

function loadscript(src, callback){
  var script = document.createElement('script');
  script.src = src
  script.async = true;
  script.onload = callback;
  document.body.appendChild(script);
};
loadscript("jquery.min", function(){for(var f in def){def[f]();}});

2

我不认为这是你的问题。默认情况下,脚本加载是同步的,因此,除非您使用defer属性或通过另一个AJAX请求加载jQuery本身,否则问题可能更像是404。您可以显示您的标记,并告诉我们如果您发现任何可疑的内容萤火虫或网页检查器?


2

让我们defer()从Dario 扩展到更可重用。

function defer(toWaitFor, method) {
    if (window[toWaitFor]) {
        method();
    } else {
        setTimeout(function () { defer(toWaitFor, method) }, 50);
    }
}

然后运行:

function waitFor() {
    defer('jQuery', () => {console.log('jq done')});
    defer('utag', () => {console.log('utag done')});
}

1

检查一下:

https://jsfiddle.net/neohunter/ey2pqt5z/

它将创建一个伪造的jQuery对象,该对象允许您使用jquery的onload方法,并且将在加载jquery后立即执行它们。

这不是完美的。

// This have to be on <HEAD> preferibly inline
var delayed_jquery = [];
jQuery = function() {
  if (typeof arguments[0] == "function") {
    jQuery(document).ready(arguments[0]);
  } else {
    return {
      ready: function(fn) {
        console.log("registering function");
        delayed_jquery.push(fn);
      }
    }
  }
};
$ = jQuery;
var waitForLoad = function() {
  if (typeof jQuery.fn != "undefined") {
    console.log("jquery loaded!!!");
    for (k in delayed_jquery) {
      delayed_jquery[k]();
    }
  } else {
    console.log("jquery not loaded..");
    window.setTimeout(waitForLoad, 500);
  }
};
window.setTimeout(waitForLoad, 500);
// end



// now lets use jQuery (the fake version)
jQuery(document).ready(function() {
  alert('Jquery now exists!');
});

jQuery(function() {
  alert('Jquery now exists, this is using an alternative call');
})

// And lets load the real jquery after 3 seconds..
window.setTimeout(function() {
  var newscript = document.createElement('script');
  newscript.type = 'text/javascript';
  newscript.async = true;
  newscript.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js';
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(newscript);
}, 3000);


这是用于注册文档就绪功能的
简便

0

有关此处使用负载setTimeout或负载的方法的切记setInterval。在这种情况下,有可能再次执行检查时,DOM已被加载,并且浏览器的DOMContentLoaded事件将被触发,因此您无法使用这些方法可靠地检测到该事件。我发现jQuery ready仍然可以使用,因此您可以嵌入平常的

jQuery(document).ready(function ($) { ... }

setTimeout或里面的setInterval东西,一切都应该正常进行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.