DOM执行问题的顺序

时间:2014-01-10 20:47:02

标签: javascript browser domcontentloaded

我在HEAD标记中有一些JavaScript,它在页面上的最后一个脚本(当前已解析)之前动态插入异步加载脚本标记。这个动态包含的脚本标记包含需要在DOM可用之后但在加载所有图像和脚本标记之前解析DOM的JavaScript。重要的是JavaScript在加载所有JS之前开始执行,因为如果有一个悬挂的脚本,这将导致糟糕的用户体验。这意味着我不能等待DOMContentLoaded事件发生。我没有任何灵活性来确定动态包含脚本标记的JavaScript的第一位。

我的问题是,我是否可以立即开始解析DOM,而无需等待DOMContentLoaded事件?如果没有,我有没有办法在不等待DOMContentLoaded事件的情况下执行此操作?

4 个答案:

答案 0 :(得分:1)

以下技术的行为可以安全地解析DOM ...

  1. 使用窗口加载或DomContentLoaded事件
  2. 在页面底部声明或注入您的脚本
  3. 在脚本标记上放置“async”属性
  4. 或者这样做:
  5. <script>
        setTimeout(function(){
         // script declared inside here will execute after the DOM is parsed
        },0);
    </script>
    

    此外,这些将在DOM中加载 NOT BLOCK 页面。

    在任何DOM下面声明脚本时,无需调用DomContentLoaded事件,除非您需要进行大小计算或定位,因为如果未指定宽度/高度,图像/视频将更改内容的大小。

    以下是一些可行的方案。

    DEPENDENT DOM IS ABOVE
    <script src="jquery.js"></script>
    <script>
        $('mydom').slideDown('fast');
    </script>
    

    或试试这个:

    <script>
        // won't fart
        setTimeout(function(){ console.log(document.getElementById('mydom').innerHTML); },0);
    </script>
    DEPENDENT DOM IS BELOW or ABOVE (dont' matter)
    

    这是我的小测试,你看到setTimeout工作是我直到最近才注意到的那些奇怪的东西,所以很高兴看到它的一个有用的例子。

    http://jsfiddle.net/FFLL2/

答案 1 :(得分:1)

  

... HEAD中的JavaScript ...在页面上的最后一个脚本之前动态插入异步加载脚本标记...

我假设the loader script is inline,意味着突出显示的位实际上是指“当前”脚本元素,即加载器。发生这种情况是因为只解析和解释了加载器脚本标记之前的html,因此插入的脚本标记实际上仍然在head而不在页面底部。因此,目标脚本仅限于对前面的元素执行DOM操作,除非您将代码包装到DOM准备好的回调中......这是您首先想要避免的!

基本上你想要加载所有的html以便页面可见/可扫描,开始加载图像/样式表(发生在非阻塞线程中),然后加载任何javascript。一种方法是put your target script at the bottom of the page,只需正确选择订单(首先是交互性,第二次增强,第三方分析/社交媒体集成/其他任何超重的最后)并根据您的需求进行调整。技术上it still blocks the page load,但是页面底部只留下了脚本(因为它们位于底部,你可以在加载DOM时直接操作DOM,减去一些IE7怪癖) 。

有一个相关的rant/overview我想链接到它提供了不错的示例和一些关于使用和滥用DOM准备回调的时间琐事,以及"other side of the story"关于为什么恒星性能 的价值低于理智的依赖管理框架。后者的主题过于宽泛而不能在一个答案中用尽,但像requirejs documentation这样的东西应该让你对模式的运作方式有一个很好的了解。

或许另一种需要考虑的模式是构建SPA - 单页面应用程序,它利用对内容块的异步访问,而不是在完整页面之间进行“传统”导航。该模式伴随着not having to parse and re-execute shared javascript on every page低估但相当显着的性能优势,这也将解决您对第三方js性能的(有效)关注。毕竟,只有a good caching policy会为加载时间创造奇迹,但是糟糕的javascript代码或大量框架的执行开销仍然存在。

更新:想出来了。考虑到您的特定方案(即无法控制标记本身,并且希望成为最后一个执行的脚本),您应该将异步脚本元素的插入到中,并将其包装为0ms {{ 1}}回调:

setTimeout

由于环境的单线程特性,一旦线程不再忙,js setTimeout(function(){ //the rest is how GA operates var targetScript = document.createElement('script'); targetScript.type = 'text/javascript'; targetScript.async = true; targetScript.src = 'target.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(targetScript, s); }, 0); 回调基本上被添加到队列中0ms延迟执行 ({ {3}})。所以浏览器甚至不知道需要加载,更不用说执行目标脚本,直到完成所有“更高优先级”的代码!并且由于DOM在添加脚本标记时可以运行,因此您不必在目标脚本本身中明确地检查它(这对于从缓存中“立即”加载时很方便。)

答案 2 :(得分:0)

是的,您应该等待用户代理告诉您DOM已加载。 但是,这样做的方法不止一种。

onreadystatechange与交互式 DOMContentLoaded 之间存在差异。

根据http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html 用户代理在停止解析文档后所做的第一件事就是设置 文件准备到&#34;互动&#34;。

用户代理不会等待加载脚本。

当文档就绪状态发生变化时,用户代理将触发Document对象的readystatechange。

因此,如果您担心的脚本是非内联的,那么您可能会与readystatechange挂钩。

谈论跨浏览器:这是规范。

答案 3 :(得分:0)

我强烈建议您完整阅读以下文章,该文章详细介绍了脚本加载和实际DOM准备情况的秘密,以及何时可以安全地执行操作,同时还要考虑浏览器的不一致。

http://www.html5rocks.com/en/tutorials/speed/script-loading/