我在HEAD
标记中有一些JavaScript,它在页面上的最后一个脚本(当前已解析)之前动态插入异步加载脚本标记。这个动态包含的脚本标记包含需要在DOM可用之后但在加载所有图像和脚本标记之前解析DOM的JavaScript。重要的是JavaScript在加载所有JS之前开始执行,因为如果有一个悬挂的脚本,这将导致糟糕的用户体验。这意味着我不能等待DOMContentLoaded
事件发生。我没有任何灵活性来确定动态包含脚本标记的JavaScript的第一位。
我的问题是,我是否可以立即开始解析DOM,而无需等待DOMContentLoaded
事件?如果没有,我有没有办法在不等待DOMContentLoaded
事件的情况下执行此操作?
答案 0 :(得分:1)
以下技术的行为可以安全地解析DOM ...
<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工作是我直到最近才注意到的那些奇怪的东西,所以很高兴看到它的一个有用的例子。
答案 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/