[javascript] How to make script execution wait until jquery is loaded

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

});

This question is related to javascript jquery

The answer is


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

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.


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

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.

_x000D_
_x000D_
// This have to be on <HEAD> preferibly inline_x000D_
var delayed_jquery = [];_x000D_
jQuery = function() {_x000D_
  if (typeof arguments[0] == "function") {_x000D_
    jQuery(document).ready(arguments[0]);_x000D_
  } else {_x000D_
    return {_x000D_
      ready: function(fn) {_x000D_
        console.log("registering function");_x000D_
        delayed_jquery.push(fn);_x000D_
      }_x000D_
    }_x000D_
  }_x000D_
};_x000D_
$ = jQuery;_x000D_
var waitForLoad = function() {_x000D_
  if (typeof jQuery.fn != "undefined") {_x000D_
    console.log("jquery loaded!!!");_x000D_
    for (k in delayed_jquery) {_x000D_
      delayed_jquery[k]();_x000D_
    }_x000D_
  } else {_x000D_
    console.log("jquery not loaded..");_x000D_
    window.setTimeout(waitForLoad, 500);_x000D_
  }_x000D_
};_x000D_
window.setTimeout(waitForLoad, 500);_x000D_
// end_x000D_
_x000D_
_x000D_
_x000D_
// now lets use jQuery (the fake version)_x000D_
jQuery(document).ready(function() {_x000D_
  alert('Jquery now exists!');_x000D_
});_x000D_
_x000D_
jQuery(function() {_x000D_
  alert('Jquery now exists, this is using an alternative call');_x000D_
})_x000D_
_x000D_
// And lets load the real jquery after 3 seconds.._x000D_
window.setTimeout(function() {_x000D_
  var newscript = document.createElement('script');_x000D_
  newscript.type = 'text/javascript';_x000D_
  newscript.async = true;_x000D_
  newscript.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js';_x000D_
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(newscript);_x000D_
}, 3000);
_x000D_
_x000D_
_x000D_


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?


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

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.


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

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

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

    }
  });
})();

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

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

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

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