使用内联脚本时未定义动态加载的jQuery

时间:2014-03-27 15:00:38

标签: javascript jquery html

情况:

  • jQuery通过html文件的javascripts.js部分中的一个文件<head>与其他脚本一起动态加载
  • 每个html文件都在html文件的jQuery(document).ready()部分的<body>上执行了自己的javascript代码

问题:

  • 错误:<body>部分中的javascript未定义 jQuery
  • 修改html文件不是一个选项(具有相同问题的+1000个文件)

示例html文件:

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

3 个答案:

答案 0 :(得分:3)

正如评论中许多人所指出的那样,这种情况正在发生,因为你是异步加载jQuery的。异步意味着执行其余代码,因此在环境中存在jQuery之前,您的文档就绪处理程序(DRH)行正在运行。

这是真正 hacky解决这个问题的方法。它涉及到jQuery的临时替代,它的工作只是记录DRH回调,直到jQuery到达。当它发生时,我们依次将它们传递给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!'); });

小提琴:http://jsfiddle.net/y7aE3/

请注意,在此示例中drh_callbacks是全局的,这显然很糟糕。理想情况下将其挂在命名空间或其他东西上,例如mynamespace.drh_callbacks

答案 1 :(得分:1)

我相信这个简单的解决方案应该可以解决问题。 html中更改的行将jquery onload函数更改为常规函数。 jquery onload函数有时会在加载jquery之前发生,我们不能这样做。它不可靠。我们需要该函数不在页面加载时执行,但是在jquery加载之后。

为此,我在javascript.js中添加的三行都在jQuery加载完成后立即执行的代码中。他们测试是否已经定义了pageLoaded函数(所以你不必在每个页面上放置一个,只有需要它的那个),然后在那里执行它。

现在,因为对HTML的更改很简单,您只需进行正则表达式搜索并替换这1000个文件即可修复它们。 Sublime,Eclipse或TextPad等工具适用于该任务。

干杯!

示例html文件:

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

答案 2 :(得分:0)

您应该尝试以下解决方法来同步加载脚本:

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

需要注意:在完全呈现DOM之前,应始终调用此函数。