学习js的时候,经常会遇到这样的问题,如何控制dom、js在页面上的加载顺序。Peter Michaux 有一篇文章非常具体地分析了各种控制 js 装载过程的方法和优劣。首先可能会用到 defer来强制页面加载完成后来再运行js,像这样:
<script src="x.js" type="text/javascript" defer></script>看似用起来没什么问题,但是发现无法兼容Mozilla。
既然这样不行,就只好换种方法,利用 window.onload 来捕获页面的载入事件。在js文件里:
window.onload = function () {alert("load over" );}这一切的一切想必大家都是很熟悉的了,但是,之后就没有问题了么?假设页面dom里有一张图片,像这样:
<img src="picture.jpg" >而这张图片又非常之大,那么在dom加载完毕之前,js是无法执行的。问题就在于假设dom提供了用户交互的功能。例如按钮,输入表单等,这个时候他们已经是被呈现了的,因此就很有可能产生无效的用户行为。我们不能指望用户会安分守己地等待页面显示加载完毕后再发生动作,而要把用户考虑成随时随刻会到处乱点的朋友。这个问题又如何解决呢?既然我们需要页面结构输出后执行js,我们不妨把js入口函数定义在页面最下面好了。
<head> <script src="x.js" type="text/javascript"></script> </head> <body> ...... <img src="picture.jpg" > <script type="text/javascript">init();</script> </body>这样就达到我们的目的了,页面结构输出完毕后就执行js,不用考虑图片的加载。但是在文档末尾嵌入一条js脚本,毕竟容易被忽略,把关键的程序入口放在这种渺小的角落,总觉得不太合适。那有什么预留退路的方法没有呢?我们可以把结尾的脚本稍微修改一下:
<head> <script src="x.js" type="text/javascript"></script> </head> <body> ...... <img src="picture.jpg" > <script type="text/javascript">window.onload();</script> </body>而在js里预先把入口定义给onload事件:
window.onload = function () {alert("load over" );}
这时候页面结构加载完毕后就会调用onload函数,而即使漏写了dom里的onload入口,js自身里的onload定义也会在页面加载完毕后执行,这样退路就留出来了。不过这时候有个问题,onload事件会执行两次,可以在js的onload实现里解决这个问题,改成这样:
var flag = false ;window.onload = function () { if (flag) {return ;} flag= true ; alert("load over" );}这样似乎已经解决我们所有的问题了,不过仍然有些小遗憾,因为最后一行代码,致使行为与结构没有分离开来,要 unobtrusive 就要 unobtrusive 的彻底,为了达到完美的分离,还有很大的讨论空间。而对于js文件内部的onload事件,我们还可以参考 Simon Willison 的addLoadEvent函数来优化:
function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function' ) { window.onload = func; } else { window.onload = function () { if (oldonload) { oldonload(); } func(); } }}然后,我们就可以在js里肆无忌惮地不停地将各个不同的函数添加到onload事件响应中了:
addLoadEvent(funcA);addLoadEvent(funcB);addLoadEvent(funcC);当然,同一个js里设置多个onload响应函数其实没什么必要,我们完全可以把funcA、funcB、funcC封装在一个函数里add,addLoadEvent函数,更理想的状态是为页面动态调用的多个js文件添加入口。