137

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

});
8
  • 4
    are you using $(document).ready(function(){...}); ? Commented Sep 20, 2011 at 13:54
  • If you're loading scripts with simple <script src="..."> tags, that cannot happen. Are you using something like RequireJS or LABjs? Commented Sep 20, 2011 at 13:55
  • are you running subsequent script in the $(document).ready() or $(window).load()? Could we see an example? Commented Sep 20, 2011 at 13:56
  • 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 Commented Sep 20, 2011 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. Commented Sep 20, 2011 at 14:25

15 Answers 15

240

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");
});
Sign up to request clarification or add additional context in comments.

5 Comments

This did not work for me. Inside of method, $ was not defined... not sure why yet.
@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.
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.
isn't there a risk of a stack overflow in case the lib doesn't load for some reason?
you should use setinterval then use clearinterval when it is loaded
39

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

2 Comments

You will be rewarded bonus points by demonstrating if this can be written as an anonymous function.
This elegant solution works fantastic for other javascript loading situations as well, in addition to jquery. thank you for sharing this!
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
});

1 Comment

This is ok as long you have a single dependency
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);
})();

Comments

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.

4 Comments

this works but you will probably see a flash of unstyled content if your jquery changes the appearance of the page
You can add an event listener to avoid overriding other scripts: stackoverflow.com/a/15564394/32453 FWIW'
@rogerdpack agree, it is better solution.
out of experience, that was always the best solution I used.
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);

5 Comments

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.
Nice and elegant -- I like how you pass the load function but then also reuse it in setTimeout.
Is there any way to eliminate using "load" and write it as an anonymous function?
@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 - 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.
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

Comments

8

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

    }
  });
})();

6 Comments

this would be vaguely useful if jQuery actually defined a window property, but it doesn't seem to?
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.
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 </body> instead of before </head>
@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.
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.
|
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();

6 Comments

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
i disagree with you @Sander this should work as loading is synchronous
you are right, my assumption about them being loaded asynchronous if they come from a different domain is completely wrong! excuse me for that
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.
@SamStoelinga: Thanks, this should now be fixed.
|
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();});

1 Comment

Or move the jquery load to the top :) stackoverflow.com/a/11012073/32453
5

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

Comments

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

Comments

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?

1 Comment

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).
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);

1 Comment

this is a no hassle solution for registering document ready functions thanks
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.