执行通过DOM插入加载的javascript

Pet*_*teM 6 javascript events dom asynchronous

我正在开发一些将小部件添加到客户网站的东西,我想异步加载我的js,以免阻止客户的页面加载.我一直在阅读很多有关此问题的主题,并且一直在尝试实现此处建议的模式,因为我的项目非常相似:

http://friendlybit.com/js/lazy-loading-asyncronous-javascript

我遇到的问题是我动态加载的javascript文件中的代码没有被执行.很抱歉,如果这似乎是一个重复的问题,但我花了几个小时搜索并尝试稍微不同的技术,我读过很多帖子,包括这些stackoverflow问题:

但是我仍然在努力做这项工作,所以我希望如果我直接问这个问题,有人可以帮助我!

我目前得到以下作为一个非常基本的测试; 很明显,我的真实剧本中还有很多内容,但我只需要了解这里发生的事情(或者事实上,未发生的事情).

所以在我的代码文件"test.js"中,我只需:

run_test = function () {
    alert('hello from test');
};
Run Code Online (Sandbox Code Playgroud)

然后在我的页面上:

(function () {
     load_test = function () {
        var t = document.getElementsByTagName('script')[0];
        var s = document.createElement('script');
        s.type = 'text/javscript';
        s.async = true;
        s.src = 'test.js';

        s.onload = s.readystatechange = function () {
            if (run_test) run_test();

            s.onload = null;
            s.onreadystatechange = null;
        };

        t.parentNode.insertBefore(s, t);
    };

    load_test();
})();
Run Code Online (Sandbox Code Playgroud)

我已经尝试了几种变体 - 我已经尝试删除"s.async = true"只是为了看看它是否有所作为,但事实并非如此.我最初有以下代替"load_test();",正如我在上面提到的第一篇文章中所建议的那样:

window.attachEvent ? window.attachEvent('onload', load_test) : window.addEventListener('load', load_test, false);
Run Code Online (Sandbox Code Playgroud)

但结果总是一样的,我从来没有看到消息"你好,从测试".事实上,我甚至可以在load_test函数中添加警报 - 如果我在行"s.onload = s.readystatechange ..."之前发出警报."我看到了该消息,但该onload函数中的警报从未出现.因此,似乎动态添加的脚本的onload永远不会触发.

BTW作为旁白 - 可能或可能不相关,但我通常在Firefox中测试,如果我在firebug中查看html,我看到test.js脚本已经加载,但如果我扩展该节点我只看到消息"重新加载页面以获取...的源代码".无论我重新加载页面多少次,我都看不到代码.我尝试在其他浏览器中测试具有相同的结果.

不禁感到我在这里缺少一些基本的东西; 任何帮助将非常感谢!

皮特


感谢大家的投入.

@zzzzBov,感谢这个例子,虽然我不确定我是否完全理解 - 我认为"onload"会在脚本完成加载后触发一次,就像将代码附加到页面的onload事件一样.我对"onreadystatechange"的理解是,它只是为了在IE中捕获同样的东西.

在回复您的注释时,新脚本将插入到原始脚本块之前的页面头部(使用insertBefore语句)(假设原始脚本块位于头部,它就是这样).

关于test.js路径,我省略了路径只是为了简化示例.我的脚本之路绝对正确; 我可以通过firebug看到脚本实际上已添加(到头部).

我的问题是,在加载脚本之后,它根本无法运行,但我认为我实际上遇到了一些缓存问题,因为我已经使用上面发布的第一个链接中描述的模式工作了(这里又是好的措施:http://friendlybit.com/js/lazy-loading-asyncronous-javascript/).

所以我的代码是这样的:

(function () {
    var addscript = function () {
        var h = document.getElementsByTagName('head')[0],
            s = document.createElement('script');
        s.type = "text/javascript";
        s.async = true;
        s.src = "myscript.js";
        h.appendChild(s);
    };
    window.attachEvent ? window.attachEvent('onload', addscript) : 
        window.addEventListener('load', addscript, false);
})();
Run Code Online (Sandbox Code Playgroud)

如果你检查那篇文章的评论,我认为在某处解释为什么仍然包含"s.async = true"是个好主意,即使在这种情况下脚本附加到窗口onload事件.

我的"真正的"主脚本确实需要jQuery,所以我认为我的最终解决方案是使用这样的东西加载jQuery,然后一旦我知道加载了,让jQuery完成加载我需要的任何其他脚本的工作.

再次感谢您的帮助.

皮特

zzz*_*Bov 5

您的脚本有一些问题。这是一个应该工作的。

function loadScript(src, callback)
{
  var s, r;
  r = false;
  s = document.createElement('script');
  s.type = 'text/javascript';
  s.src = src;
  s.onload = s.onreadystatechange = function() {
    //console.log( this.readyState ); //uncomment this line to see which ready states are called.
    if ( !r && (!this.readyState || this.readyState == 'complete') )
    {
      r = true;
      callback();
    }
  };
  document.body.appendChild(s);
}
Run Code Online (Sandbox Code Playgroud)

您的load_test函数的问题在于它会run_test()在新脚本执行之前调用。您的脚本将删除onloadonreadystatechange事件回调在第一onreadystatechange这是典型的事件loading

此外,async应该是不必要的,因为新添加的脚本将插入到document.body任何可能的末尾。如果您想在页面的其余部分之后加载脚本,然后等待页面的其余部分加载(body.onloaddocument.onreadystatechange之前调用)loadScript

您的脚本的最大问题听起来test.js似乎不存在于它被调用的路径中。确保添加<script type="text/javascript" src="test.js"></script>内联确实有效。