JavaScript页面文档初始化 window.onload的替代办法

本文介绍了改进的JavaScript初始化装载方法,解决了window.onload在IE环境下只执行最后一个绑定函数的问题,并对比了不同解决方案的优劣。

很早,大概去年就在mg12的博客看到他一篇很久以前的文章《 JavaScript 初始化装载方法 》,当时也没注意。这两天在做页面一些部分的延迟加载时,又想起了这个,因为使用window.onload,实在是一个很不明智的选择,很可能一个坏掉的图片或什么的就会导致你的JS迟迟装载不了,无法执行。

我试用了一下mg12文章中的方法,发现一个严重的错误,那就是在IE下如果同时绑定多个函数,那么就只会有最后一个执行。仔细分析了一下,原来是因为代码中判断IE页面文档初始化完毕时会向页面中写入一个

   1: <script id="__ie_onload" defer src="javascript:void(0)"></script>

而绑定多个函数时,就会向页面中写入多个上面的标签,由于页面上只允许一个id存在,所以通过document.getElementById(“__ie_onload”);自然就只能获得一个对象,所以只会执行一个绑定函数了。

我对其稍微进行了改造,无非就是加个数值变量,每次写入时让ID不同。

   1: var _flag=1;//标志整型变量
   2: function documentReady(fun){
   3:     if (document.addEventListener) {
   4:         document.addEventListener("DOMContentLoaded", fun, false);
   5:     } else if (/MSIE/i.test(navigator.userAgent)) {
   6:         document.write('<script id=__ie_onload'+_flag+' defer src=//0><\/script>');
   7:         var script = document.getElementById("__ie_onload"+_flag);_flag++;
   8:         script.onreadystatechange = function() {
   9:             if (this.readyState == 'complete') {
  10:                 fun();
  11:             }
  12:         }
  13:     } else if (/WebKit/i.test(navigator.userAgent)) {
  14:         var _timer = setInterval( function() {
  15:             if (/loaded|complete/.test(document.readyState)) {
  16:                 clearInterval(_timer);
  17:                 fun();
  18:             }
  19:         }, 10);
  20:     }
  21: }

这样如果绑定多个函数,就会在页面上依次写入id=”__ie_onload1”、id=”__ie_onload2”。。。等<script>标签了。

如此一来,即使绑定多个函数也可以顺利执行了。

后话:今天闲暇时,又上网搜了一下这方面的东西,发现早在06年就有人贴出了几近完美的解决方法,而且他们也考虑到了多个事件函数执行的问题,通过栈结构解决的。它是将绑定的所有函数存入一个栈中,判断文档下载完成后,再从栈中依次取出函数执行,相比于我上面的方法,判断文档是否完成只需要一次即可,无论绑定了多少个函数都是只需一次,而我的就需要多次了。看样子mg12看的书貌似是抄袭人家的,也不知为何进行了精简,结果还给精简错了,囧。。原始网页在http://www.thefutureoftheweb.com/blog/adddomloadevent,有兴趣围观。

下面是文章的代码去掉注释后

   1: addDOMLoadEvent = (function(){
   2:     var load_events = [],load_timer,script,done,exec,old_onload,
   3:         init = function () {
   4:             done = true;
   5:             clearInterval(load_timer);
   6:             while (exec = load_events.shift())
   7:                 exec();
   8:             if (script) script.onreadystatechange = '';
   9:         };
  10:     return function (func) {
  11:         // if the init function was already ran, just run this function now and stop
  12:         if (done) return func();
  13:         if (!load_events[0]) {
  14:             // for Mozilla/Opera9
  15:             if (document.addEventListener)
  16:                 document.addEventListener("DOMContentLoaded", init, false);
  17:             // for Internet Explorer
  18:             /*@cc_on @*/
  19:             /*@if (@_win32)*/
  20:             if (/MSIE/i.test(navigator.userAgent)){
  21:                 document.write("<script id=__ie_onload defer src=//0><\/scr"+"ipt>");
  22:                 script = document.getElementById("__ie_onload");
  23:                 script.onreadystatechange = function() {
  24:                     if (this.readyState == "complete")
  25:                         init(); // call the onload handler
  26:                 };
  27:             }
  28:             /*@end @*/
  29:             // for Safari
  30:             if (/WebKit/i.test(navigator.userAgent)) { // sniff
  31:                 load_timer = setInterval(function() {
  32:                     if (/loaded|complete/.test(document.readyState))
  33:                         init(); // call the onload handler
  34:                 }, 10);
  35:             }
  36:             old_onload = window.onload;
  37:             window.onload = function() {
  38:                 init();
  39:                 if (old_onload) old_onload();
  40:             };
  41:         }
  42:         load_events.push(func);
  43:     }
  44: })();

具体的效果,可以到我的友情链接页面查看。这个页面有许多favicon图标需要从不同的地址下载,需要时间较长,而使用本文中的初始化装载方法后,并不会因为图标的未下载而影响我js代码的执行。大家可以很顺利的看到边栏各个栏目被加载上(:边栏应用了延迟加载)。

12

### 解析 `window.onload` 首次加载不触发的原因 当讨论 `window.onload` 是否会在首次加载页面时触发,实际上按照标准行为,`window.onload` 应该在页面资源完全加载完毕后触发一次。然而,在某些特定场景下,比如通过浏览器缓存快速访问或是单页应用(SPA)环境中,可能会遇到预期之外的行为。 对于 SPA 如 Vue.js 构建的应用而言,由于其动态更新部分视图而无需重新加载整个 HTML 文档的特点,可能导致 `onload()` 在初次渲染前过早调用[^3]。这使得一些初始化逻辑未能按计划执行。 另外需要注意的是不同浏览器处理页面导航的方式存在差异,特别是涉及前进、后退以及刷新操作时,可能影响到诸如 `onload` 这样的事件监听器能否正常工作[^4]。 ### 提供解决方案 为了确保无论何时何地都能可靠捕获页面真正意义上的“首次加载”,建议采用如下策略: #### 使用 `pageshow` 代替 `onload` 考虑到 `onpageshow` 事件不仅适用于初始加载还会响应历史记录变化带来的页面显示情况,因此更适合用来替代传统的 `onload` 来实现更广泛的覆盖范围[^1]。 ```javascript window.addEventListener('pageshow', function(event) { // 判断是否为第一次加载而非从缓存恢复 if (!event.persisted || !sessionStorage.getItem('hasLoaded')) { sessionStorage.setItem('hasLoaded', 'true'); // 执行仅需运行一次的任务 console.log("This is the first time loading."); } }); ``` 此方法利用了 `Event.persisted` 属性来区分真正的首屏呈现还是因缓存机制引起的重现,并借助 `sessionStorage` 记录状态防止重复动作。 #### 结合 `performance.navigation.type` 检测加载模式 另一种方式则是基于现代浏览器提供的性能 API 中关于当前页面是如何被访问的信息来进行判断并采取相应措施[^2]。 ```javascript if (typeof window.performance !== "undefined") { switch(performance.navigation.type){ case performance.navigation.TYPE_NAVIGATE: console.log("Normal page load"); break; case performance.navigation.TYPE_RELOAD: console.log("Page reload detected"); break; default: console.log("Other types of navigation occurred"); } } ``` 这种方法能够帮助识别不同的浏览行为类别,从而允许开发者针对每种情形定制化处理流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值