window.onload 不触发的解决办法

5,659 阅读5分钟

踩坑经历

昨天我在写前端代码时,有个需求是:提交表单后,后台返回一个数据到前端页面的隐藏域中,在这个页面加载完成后,判断这个隐藏域中是否有内容,有则弹出后台返回的信息。我首先想到的就是用 window.onload = function(){...} 的方式在页面加载完后实现对应的操作。当我测试功能的时候,竟翻车了,window.onload 里面的内容完全不触发。我也做了其它的测试,包括使用 jQuery 的 ready() 方法和 jQuery 自执行的方法来测试,这些方法能执行,但还是达不到我想要的效果,因为执行这些方法时,我后台的返回的数据还没有渲染到DOM中。

下面是测试时截图:

  • 使用 alert() 阻塞来判断 ready() 执行时,页面情况

image-20200609143200738

  • alert() 阻塞时 DOM 元素显示如下,head 和 body 是空的(可以看到我当时在找着解决方案,捂脸.jpg)

image-20200609143133634

上面的代码,如果是使用 window.onload,是完全不执行

我也从浏览器页面加载去想这个问题,但仍然想不到哪里出问题了,因为我的 js 代码都写在 body 的最下面。

后来找到一篇博客,说页面元素如果有误,那么 window.onload 将不会触发,博客的网址我忘记记录了,但我确定我的 html 和 css 代码是没问题的(因为我是用原本成功的页面删减了一些没用标签,就这一个页面用同样的逻辑写 window.onload 的方法出问题了)。自己瞎鼓捣了挺久,特别是去检查了后台数据的传参(虽然当时知道可能行不大,但万一呢)。后来我想到了这篇博客说的页面元素有错误,然后我突然有个想法,看看是不是我哪个js 方法写错了,然后把除 ready()window.onload 后面的全部方法都注释掉了,就打算看看这两个方法会不会触发。果不其然,它成功运行并达到效果了,困扰我半天的问题终于解决了。顺着这个思路下去,寻根溯源,我逐个把每个方法给放出来,最后终于找到了问题所在!我之前写过了一个 window.onload 的方法!两个window.onload 方法,默认会执行后面那个方法,所以我之前怎么测试都不触发的原因就是因为写了多个 window.onload 方法。(之前 js代码 是直接复制过来修改的,没有注意到这个细节,而且这两个 window.onload 相隔了很多 js 方法)

那为什么 ready() 方法不能将后台的返回到的数据渲染到 DOM 呢?

那就得从 window.onload 和 jQuery 的 ready() 方法的区别来讲了。

  • window.onload

    所有元素加载完毕后才执行,包括图片、CSS等所愿元素,无许考虑加载的次序问题,且只能执行一次。

  • (document).ready() 或者(function(){...}) 或者 (function(){...})(jQuery );

    DOM树加载完成后执行,不需要等到所有元素加载完毕,且可以执行多次。

如果这两个的还不是很懂的话,我们这里就讲下浏览器的加载顺序:

浏览器的加载和渲染顺序是自上而下的,遇到 link,script 等会暂停解析,将控制权交给 JavaScript 解析,执行完毕之后再交给浏览器渲染引擎。详细的渲染步骤如下:

  1. 加载 html
  2. 解析器解析html
  3. 创建 DOM 树 ,加载CSS**(加载完之后才能向下执行)**
  4. 解析 CSS,讲 CSS 作用于 DOM 节点上
  5. 遇到 js,先下载,如果操作了已经渲染完成的元素,则重新渲染
  6. 遇到图片资源,向服务器发送请求,无需等待,继续向下执行
  7. 返回图片后,重新渲染这部分代码
  8. 显示所有渲染完成的画面

从 html 渲染步骤我们可以看出,jQuery 的 ready() 方法是在第三步完成时执行的, window.onload 是在第八步时执行的。也就是说,jQuery 的 ready() 方法主要比 window.onload 先执行的,而 DOM 树加载完成后,数据并不一定会加载完成。所以我之前的代码,ready() 方法中不能将后台的返回到的数据渲染到 DOM 就是这个原因。后台返回的数据是在 DOM 树创建完成后才修改 DOM 元素的。

那么这两个方法我们要如何选择使用呢?

我个人观点是如果是:需要编写功能性代码,需要操作后台返回的数据的使用 window.onload ,因为它不需要考虑加载顺序的问题,jQuery 的 ready() 方法能做的 window.onload 基本都能做。看起来是不错,但是比较致命的还是速度问题。很多操作可能并不需要等待全部页面渲染完成,特别是 CSS、JS、图片文件比较多或这些文件比较大的页面,CSS 会阻塞页面的渲染。所以,大部分场景下,我们使用 jQuery 的 ready() 方法比较多

讲到这里,好像逐渐跑题了,那么我再来总结一下我百度找到的大部分解决方法还有我这个低级错误的解决方法吧

解决方案

  1. 检查 js 代码是否有多个 window.onloadwindow.onload 只能出现一次!

  2. 使用 jQuery 的 ready() 方法

    <script type="text/javascript">
        // 方式一
        $(document).ready(function(){
        	// 具体操作
    	});
    	// 方式二
    	$(function(){
            // 具体操作
        });
    	// 方式三
        (function(){
            // 具体操作
        })(jQuery)
        
    </script>
    
  3. 从浏览器加载顺序去思考,找出哪个加载环节出错,并对应去解决