131

I am having a problem where the page is loading so fast, that jquery hasn't finished loading before it is being called by a subsequent script. Is there a way to check for the existence of jquery and if it doesn't exist, wait for a moment and then try again?


In response to the answers/comments below, I am posting some of the markup.

The situation... asp.net masterpage and childpage.

In the masterpage, I have a reference to jquery. Then in the content page, I have a reference to the page-specific script. When the page specific script is being loaded, it complains that "$ is undefined".

I put alerts at several points in the markup to see the order in which things were firing, and confirmed that it fires in this order:

  1. Master page header.
  2. Child page content block 1 (located inside the head of the masterpage, but after the masterpage scripts are called).
  3. Child page content block 2.

Here is the markup at the top of the masterpage:

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

Then in the body of the masterpage, there is an additional ContentPlaceHolder:

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

In the child page, it looks like so:

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

Here is the content of the "../Script/Dashboard.js" file:

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

});
Amanda Kitson
  • 5,477
  • 12
  • 49
  • 73
  • 4
    are you using `$(document).ready(function(){...});` ? – rownage Sep 20 '11 at 13:54
  • If you're loading scripts with simple ` – Pointy Sep 20 '11 at 13:55
  • are you running subsequent script in the `$(document).ready()` or `$(window).load()`? Could we see an example? – John Hartsock Sep 20 '11 at 13:56
  • I can't use $(document).ready because it uses jquery... I get an error about how "$ is undefined". I am using it, but it doesn't work because jquery isn't all the way loaded yet. – Amanda Kitson Sep 20 '11 at 13:57
  • could you also show us the content of the javascript file, that gives the $ is undefined error? is that the Dashboard.js? – Sander Sep 20 '11 at 14:14
  • @Sander I added the content of the Dashboard.js file. – Amanda Kitson Sep 20 '11 at 14:19
  • 1
    @AmandaMyer now, ignore all the suggestions about using document ready, since you already are doing that, but i bet it is the mimetype that is causing them not to load synchronously – Sander Sep 20 '11 at 14:24
  • 2
    Try changing `type="text/Scripts"` to `type="text/javascript"`, or get rid of it all together it's not needed anymore, also get rid of the `language="javascript`. Might as well start trying things. – Jack Sep 20 '11 at 14:25

15 Answers15

238

Late to the party, and similar to Briguy37's question, but for future reference I use the following method and pass in the functions I want to defer until jQuery is loaded:

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

It will recursively call the defer method every 50ms until window.jQuery exists at which time it exits and calls method()

An example with an anonymous function:

defer(function () {
    alert("jQuery is now loaded");
});
Micros
  • 5,966
  • 2
  • 28
  • 34
Darbio
  • 11,286
  • 12
  • 60
  • 100
  • 2
    This did not work for me. Inside of method, $ was not defined... not sure why yet. – Aaron Lifshin May 04 '15 at 22:56
  • 1
    @gidim It won't, because it is a timer and not a loop. But it will keep running as long as the user stays on the page. – Micros May 31 '17 at 11:52
  • 1
    It would be better to investigate the loading order of scripts. I found that if the `script` tag that loads jQuery had a `defer` attribute, it would cause the problem by not loading until later, despite the code-defined order of scripts. – ADTC Jan 21 '18 at 11:43
  • isn't there a risk of a stack overflow in case the lib doesn't load for some reason? – polvoazul May 12 '21 at 06:09
36

the easiest and safest way is to use something like this:

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

        // place your code here.

        clearInterval(waitForJQuery);
    }
}, 10);
Mark
  • 6,762
  • 1
  • 33
  • 50
The Moisrex
  • 1,857
  • 1
  • 14
  • 16
26

you can use the defer attribute to load the script at the really end.

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

but normally loading your script in correct order should do the trick, so be sure to place jquery inclusion before your own script

If your code is in the page and not in a separate js file so you have to execute your script only after the document is ready and encapsulating your code like this should work too:

$(function(){
//here goes your code
});
malko
  • 2,292
  • 18
  • 26
22

Yet another way to do this, although Darbio's defer method is more flexible.

(function() {
  var nTimer = setInterval(function() {
    if (window.jQuery) {
      // Do something with jQuery
      clearInterval(nTimer);
    }
  }, 100);
})();
thdoan
  • 18,421
  • 1
  • 62
  • 57
19

You can try onload event. It raised when all scripts has been loaded :

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

But keep in mind, that you may override others scripts where window.onload using.

Nigrimmist
  • 10,289
  • 4
  • 52
  • 53
16

I have found that suggested solution only works while minding asynchronous code. Here is the version that would work in either case:

document.addEventListener('DOMContentLoaded', function load() {
    if (!window.jQuery) return setTimeout(load, 50);
    //your synchronous or asynchronous jQuery-related code
}, false);
Annarfych
  • 537
  • 4
  • 10
  • Thanks, checking once on load and only as fallback start looping every 50ms is much better than just letting it loop with setInterval like the top voted answer. In the best case you have 0ms delay, instead of the average case of 25ms of the top answer. – Luc Apr 24 '19 at 15:17
  • Nice and elegant -- I like how you pass the `load` function but then also reuse it in `setTimeout`. – Nemesarial Jan 08 '20 at 12:56
  • Is there any way to eliminate using "load" and write it as an anonymous function? – edwardsmarkf Mar 16 '21 at 23:50
  • @edwardsmarkf Actually `load` is not polluting anything here. Here `function load() {..}` is the shortcut to write it like `function(){ return load(); var load = ()=>{..} }`. Also, if you do not need the `this` context, you can replace `function load(){..}` with `(_=>_(_))(load => {..})`, to pass the function as the first parameter to itself. However I really do not see any benefit in doing so, except making it hard to read. – Tino Jun 16 '23 at 21:00
  • @tino - sorry i missed where the name "load" is indeed referenced in the function. for some reason i either thought it was some reserved word or i didn't see it. – edwardsmarkf Jun 17 '23 at 23:37
12

edit

Could you try the correct type for your script tags? I see you use text/Scripts, which is not the right mimetype for javascript.

Use this:

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

end edit

or you could take a look at require.js which is a loader for your javascript code.

depending on your project, this could however be a bit overkill

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Sander
  • 13,301
  • 15
  • 72
  • 97
7

Rather than "wait" (which is usually done using setTimeout), you could also use the defining of the jQuery object in the window itself as a hook to execute your code that relies on it. This is achievable through a property definition, defined using 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

    }
  });
})();
Protector one
  • 6,926
  • 5
  • 62
  • 86
  • this would be vaguely useful if jQuery actually defined a window property, but it doesn't seem to? – Jim Jun 08 '16 at 10:47
  • It doesn't really set a property, but it does set `window.jQuery` to a value, by defining the `jQuery` variable in the global scope (i.e. `window`). This will trigger the property setter function on the window object defined in the code. – Protector one Jun 08 '16 at 11:52
  • This doesn't work if the jQuery property gets defined before this code is executed; ie if your deferred jquery.js loads before this code is loaded. One solution is to put your deferred jquery.js before `

    ` instead of before ``

    – user36388 Aug 21 '17 at 11:40
  • @user: Simply execute the code before jQuery (deferred or otherwise) is loaded. It doesn't matter where you load it, just that my code fragment comes before it. – Protector one Aug 21 '17 at 13:04
  • 1
    This is very useful if you can guarantee that the scripts that use this come before the inclusion of jQuery in your document. None of the inefficiencies or race-case scenarios from the looping approaches above either. – MLK.DEV Jan 15 '19 at 13:41
  • You can easily fix that it works anywhere. Replace `var _jQuery;` with `var _jQuery = window.jQuery;` and just before the last line add `if (_jQuery) window.jQuery = _jQuery;` -- the only thing left is, that the code always executes when `window.jQuery` is set or changed. If that is a problem, you need to add some guard against this. However this also can be seen as feature. – Tino Jun 16 '23 at 21:10
6

It's a common issue, imagine you use a cool PHP templating engine, so you have your base layout:

HEADER
BODY ==> dynamic CONTENT/PAGE
FOOTER

And of course, you read somewhere it's better to load Javascript at the bottom of the page, so your dynamic content doesnot know who is jQuery (or the $).

Also you read somewhere it's good to inline small Javascript, so imagine you need jQuery in a page, baboom, $ is not defined (.. yet ^^).

I love the solution Facebook provides

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

So as a lazy programmer (I should say a good programmer ^^), you can use an equivalent (within your page):

window.jqReady = function() {}

And add at the bottom of your layout, after jQuery include

if (window.hasOwnProperty('jqReady')) $(function() {window.jqReady();});
Thomas Decaux
  • 21,738
  • 2
  • 113
  • 124
6

Use:

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

Check out this for more info: http://www.learningjquery.com/2006/09/introducing-document-ready

Note: This should work as long as the script import for your JQuery library is above this call.

Update:

If for some reason your code is not loading synchronously (which I have never run into, but apparently may be possible from the comment below should not happen), you could code it like the following.

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();
Dan Searle
  • 546
  • 2
  • 5
  • 14
Briguy37
  • 8,342
  • 3
  • 33
  • 53
  • 7
    this wait's untill the dom is ready, not untill jquery is loaded. he probably has 2 script files, 1 from a CDN (jquery from google and a plugin locally) where the 1 is loaded earlier than the other.... your code does not solve this, if the plugin is loaded faster than jquery itself – Sander Sep 20 '11 at 13:58
  • 2
    i disagree with you @Sander this should work as loading is synchronous – malko Sep 20 '11 at 14:02
  • you are right, my assumption about them being loaded asynchronous if they come from a different domain is completely wrong! excuse me for that – Sander Sep 20 '11 at 14:11
  • This will throw an exception if jquery is not available causing it to never go into the else statement it seems. Guess it should be a try and catch or something. – Sam Stoelinga Jun 23 '12 at 04:48
  • @SamStoelinga: Thanks, this should now be fixed. – Briguy37 Jun 25 '12 at 13:27
  • @Briguy37 you need to add a call to that checker function to get it started, e.g. runYourFunctionWhenJQueryIsLoaded(); after it's defined – Dan Searle Mar 07 '14 at 11:52
4

I'm not super fond of the interval thingies. When I want to defer jquery, or anything actually, it usually goes something like this.

Start with:

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

Then:

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

Then finally:

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

Or the less mind-boggling version:

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

And in the case of async you could execute the pushed functions on jquery onload.

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

Alternatively:

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]();}});
Simon
  • 2,449
  • 2
  • 18
  • 20
3

Let's expand defer() from Dario to be more reusable.

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

Which is then run:

function waitFor() {
    defer('jQuery', () => {console.log('jq done')});
    defer('utag', () => {console.log('utag done')});
}
mewc
  • 1,253
  • 1
  • 15
  • 24
2

Check this:

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

It will create a fake jQuery object, that allows you to use the onload methods of jquery, and they will be executed as soon as jquery is loaded.

It's not perfect.

// 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);
Arnold Roa
  • 7,335
  • 5
  • 50
  • 69
2

I don't think that's your problem. Script loading is synchronous by default, so unless you're using the defer attribute or loading jQuery itself via another AJAX request, your problem is probably something more like a 404. Can you show your markup, and let us know if you see anything suspicious in firebug or web inspector?

jmar777
  • 38,796
  • 11
  • 66
  • 64
  • Actually only the naive script loading of the old days is synchronous. If if comes to modules and frameworks, even the synchronous script load order can easily get disturbed, such that some later script suddenly loads before. It also depends on how you use jQuery (and similar) with all it's addons. So I understand `jQuery` here as "any similar framework which might load asynchronously due to some unforseen situation" (in my case: OctoKit). – Tino Jun 16 '23 at 21:19
1

A tangential note on the approaches here that load use setTimeout or setInterval. In those cases it's possible that when your check runs again, the DOM will already have loaded, and the browser's DOMContentLoaded event will have been fired, so you can't detect that event reliably using these approaches. What I found is that jQuery's ready still works, though, so you can embed your usual

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

inside your setTimeout or setInterval and everything should work as normal.

Richard J
  • 6,883
  • 4
  • 22
  • 27