2

Situation:

  • jQuery is dynamically loaded together with other scripts by one file javascripts.js in the <head> section of the html file
  • Each html file has it's own javascript code executed on jQuery(document).ready() in the <body> section of the html file

Problem:

  • Error: jQuery is not defined for javascript in the <body> section
  • Modifying the html file is not an option (+1000 files with same problem)

Example html file:

<html>
  <head>
    <title>JS test</title>
    <script src="javascripts.js" type="text/javascript"></script>
  </head>
<body>
  <input type="text" class="date">
  <script>
    jQuery(document).ready(function() {  // Error: jQuery not defined
      jQuery('.date').datepicker();
    });
  </script>
</body>
</html>

javascripts.js:

// Load jQuery before any other javascript file
function loadJS(src, callback) {
    var s = document.createElement('script');
    s.src = src;
    s.async = true;
    s.onreadystatechange = s.onload = function() {
        var state = s.readyState;
        console.log("state: "+state);
        if (!callback.done && (!state || /loaded|complete/.test(state))) {
            callback.done = true;
            callback();
        }
    };
    document.getElementsByTagName('head')[0].appendChild(s);
}

loadJS('javascripts/jquery-1.8.3.min.js', function() {
    var files = Array(
      'javascripts/functions.js',
      'javascripts/settings.js'
    );

    if (document.getElementsByTagName && document.createElement) {
        var head = document.getElementsByTagName('head')[0];

        for (i = 0; i < files.length; i++) {
            var script = document.createElement('script');
            script.setAttribute('type', 'text/javascript');
            script.setAttribute('src', files[i]);
            script.async = true;
            head.appendChild(script);
        }
    }
});
9
  • It would appear that you are loading async. If you changed this to false it would cause it to load and be available before your scripts in the page were executed. jQuery(document).ready(function() is going to fail because of the async. Commented Mar 27, 2014 at 15:03
  • "+1000 files with same problem" Well, you should then really think about using any kind of templating system then. Regarding your issue, you could try to use instead: window.onload = function(){/*jQuery code here*/}; Commented Mar 27, 2014 at 15:06
  • Tried switching async to false but didn't work. It has to be async because all the javascript files have to be loaded before any other script on the page must be executed. Commented Mar 27, 2014 at 15:07
  • 1000 seriously broken pages: Modifying the HTML may be the only option. Find faster ways of modifying the HTML instead (VS global search replace etc). Commented Mar 27, 2014 at 15:08
  • " It has to be async because" You mean sync right, or i didn't understand what you mean Commented Mar 27, 2014 at 15:09

3 Answers 3

4

This is happening, as many in the comments have pointed out, because you are loading jQuery asynchronously. Asynchronous means the rest of the code is executed, and so your document-ready handler (DRH) line is running before jQuery is present in the environment.

Here's a really hacky way of resolving this. It involves making a temporary substitute of jQuery whose job is just to log the DRH callbacks until jQuery has arrived. When it does, we pass them in turn to jQuery.

JS:

//temporary jQuery substitute - just log incoming DRH callbacks
function jQuery(func) {
    if (func) drh_callbacks.push(func);
    return {ready: function(func) { drh_callbacks.push(func); }};
};
var $ = jQuery, drh_callbacks = [];

//asynchronously load jQuery
setTimeout(function() {
    var scr = document.createElement('script');
    scr.src = '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js';
    document.head.appendChild(scr);
    scr.onload = function() {
        $.each(drh_callbacks, function(i, func) { $(func); });
    };
}, 2000);

HTML:

jQuery(document).ready(function() { alert('jQuery has loaded!'); });

Fiddle: http://jsfiddle.net/y7aE3/

Note in this example drh_callbacks is global, which is obviously bad. Ideally hook it onto a namespace or something, e.g. mynamespace.drh_callbacks.

Sign up to request clarification or add additional context in comments.

4 Comments

This is a good solution. Unfortunately I don't get the callbacks passed on to jQuery correctly. It fixes the jQuery is undefined issue, but it doesn't execute the inline scripts anymore.
Hmm, well the concept works, as the Fiddle shows. Perhaps you're binding DRH callbacks in some other way (jQuery allows several, e.g. jQuery(document).ready(function() { ... });, $(function() { ... }); etc.) I've edited the code to work with all of them.
Thanks @Utkanos, but still same result. I think the function call is not stored correctly in the drh_callbacks array. It creates an object of the type Function, but it's missing a few things: arguments: undefined, caller: undefined, length: 0, name: "", prototype: Object, __proto__: Function
Well a function reference doesn't have arguments; only a function call does. Sorry but without digging deep into your code I might have exhausted my power to help here. Hopefully you have something you can play around with, though - good luck in resolving it.
1

I believe this simple solution should do the trick. The changed line in the html changes the jquery onload function to a regular function. The jquery onload function will sometimes happen before the jquery is loaded and we can't have that. It's unreliable. We need that function not to execute on page load, but AFTER the jquery has loaded.

To that end, the three lines I've added in the javascript.js are inside the code that is executed immediately after jQuery has finished loading. They test to see if the pageLoaded function has been defined (so you don't have to put one on every page, only the ones that need it) and then execute it if it's there.

Now, because the change to the HTML is simple, you can just do a regex search and replace on those 1000 files to fix them. Tools like Sublime, Eclipse or TextPad are suited for that task.

Cheers!

Example html file:

<html>
  <head>
    <title>JS test</title>
    <script src="javascripts.js" type="text/javascript"></script>
  </head>
<body>
  <input type="text" class="date">
  <script>
    function pageLoaded() { // changed
      jQuery('.date').datepicker();
    } // changed
  </script>
</body>
</html>

javascripts.js:

// Load jQuery before any other javascript file
function loadJS(src, callback) {
    var s = document.createElement('script');
    s.src = src;
    s.async = true;
    s.onreadystatechange = s.onload = function() {
        var state = s.readyState;
        console.log("state: "+state);
        if (!callback.done && (!state || /loaded|complete/.test(state))) {
            callback.done = true;
            callback();
        }
    };
    document.getElementsByTagName('head')[0].appendChild(s);
}

loadJS('javascripts/jquery-1.8.3.min.js', function() {
    var files = Array(
      'javascripts/functions.js',
      'javascripts/settings.js'
    );

    if (document.getElementsByTagName && document.createElement) {
        var head = document.getElementsByTagName('head')[0];

        for (i = 0; i < files.length; i++) {
            var script = document.createElement('script');
            script.setAttribute('type', 'text/javascript');
            script.setAttribute('src', files[i]);
            script.async = true;
            head.appendChild(script);
        }
    }
    if( typeof(pageLoaded) == "function" ){  // added
        pageLoaded();  // added
    }  // added
});

Comments

0

You should try following workaround to load scripts synchronously:

function loadJS(src, callback) {
    document.write('<scr'+'ipt src="'+src+'"><\/scr'+'ipt>');
    callback();
}

IMPORTANT to note: this function should be called always before DOM is fully rendered.

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.