$(document).ready() 方法是否也代表 CSS 就绪?

51

我有一个脚本在$(document).ready()上执行,用于垂直对齐我的布局中的块级元素。90%的时间,它工作得很好,没有任何问题。然而,在额外的10%中,会出现以下两种情况之一:

  • 在居中过程中,显然存在延迟,块级元素会跳到位置。这可能仅与性能有关-因为页面大小通常很大,并且有相当多的javascript同时执行。

  • 居中将完全混乱,块级元素将被推下太远或不够远。似乎尝试计算高度,但获得了不正确的测量结果。

是否有任何原因导致在DOM-ready上执行脚本时,还没有所有正确的CSS值注入到DOM中?(所有CSS都通过在中呈现)。

此外,这是引起问题的脚本(是的,它直接从这里拿出来的):

 (function ($) {
    // VERTICALLY ALIGN FUNCTION
    $.fn.vAlign = function() {
      return this.each(function(i) {
        var ah = $(this).height();
        var ph = $(this).parent().height();
        var mh = (ph - ah) / 2;
        $(this).css('margin-top', mh);
      });
    };
  })(jQuery);

谢谢。


是否涉及到图片?即使计算样式后,图片加载也可能导致重新布局。 - sam
5个回答

64

1.3版本发布说明中:

ready()方法不再保证等待所有样式表加载完毕。相反,应该在页面上的脚本之前包含所有CSS文件。更多信息

ready(fn)文档中:

注意:请确保在脚本之前(尤其是调用ready函数的脚本)包含所有样式表。这样做将确保在jQuery代码开始执行之前正确定义所有元素属性。如果不这样做,将会导致零星的问题,特别是在基于WebKit的浏览器(如Safari)上。

请注意,以上内容甚至与实际的CSS渲染无关,因此当ready()启动时,您仍然可能看到屏幕发生变化。但它应该可以避免问题。

实际上,我发现仅仅将CSS放在JS之上就可以解决所有问题有点奇怪。CSS是异步加载的,因此在CSS仍在下载时,JS加载可以开始并完成。因此,如果以上是一个解决方案,那么执行任何JS代码都会被暂停,直到所有较早的请求完成?

我进行了一些测试,确实,有时候JS会被延迟到CSS加载完毕之后。我不知道为什么,因为瀑布显示JS已经完成加载,而下载CSS还没有完成。

请查看 JS Bin 获取一些HTML其结果(这有10秒的延迟),并查看 webpagetest.org 获取其瀑布图结果。这使用了 Steve Souders 的 cuzillion.com 中的一些脚本来模拟缓慢响应。在瀑布图中,对 resource.cgi 的引用是 CSS。因此,在 Internet Explorer 中,第一个外部 JS 在请求 CSS 后开始加载(但该 CSS 将需要另外 10 秒才能完成)。但是,第二个 <script> 标签也要等到 CSS 加载完成后才会执行:

<link rel="stylesheet" type="text/css" href=".../a script that delays.cgi" />

<script type="text/javascript" src=".../jquery.min.js"></script> 

<script type="text/javascript"> 
  alert("start after the CSS has fully loaded"); 
  $(document).ready(function() { 
    $("p").addClass("sleepcgi"); 
    alert("ready"); 
  });         
</script> 

Waterfall with a single external JS script

另一个测试,在获取jQuery之后,使用第二个外部JS展示了在CSS加载完成之前不会开始下载第二个JS的情况。这里,对resource.cgi的第一次引用是CSS,第二次是JS:

Waterfall with two external JS scripts

将样式表移到所有JS下方确实显示JS(包括ready函数)运行得更早,但即使如此,在Safari和Firefox中,我的快速测试中正确使用了jQuery应用的类 - 当JS运行时仍未知。 但是,这时候像 $(this).height()之类的东西会产生错误的值是有道理的。
然而,额外测试显示 JS被暂停直到先前定义的CSS加载完毕并不是一条通用规则。似乎与使用外部JS和CSS有关。我不知道这是如何工作的。

最后说明:由于JS Bin在每个脚本中包含Google Analytics,当从裸URL(如jsbin.com/aqeno)运行时,测试结果实际上会被JS Bin更改...似乎编辑URL的输出选项卡(例如jsbin.com/aqeno/edit)不包括其他Google Analytics内容,并且肯定会产生不同的结果,但是使用webpagetest.org测试该URL很困难。strager提供的Firefox中样式表块下载和IE中JavaScript执行的参考是更好地理解的良好起点,但我还有许多问题...还要注意Steve Souders' IE8 Parallel Script Loading使事情变得更加复杂。(上面的瀑布是使用IE7创建的。)

也许人们应该简单地相信发布说明和文档...


Cuzillion(http://stevesouders.com/cuzillion/)能帮助你理解不同类型的对象在不同的浏览器中如何加载。 - strager
...但这仍然不是最终答案。也许事情是否在<head><body>中甚至很重要?鉴于Firefox中的样式表块下载和IE中的JavaScript执行(那其他浏览器呢?)以及IE8并行脚本加载,我猜这个垂直对齐脚本可能会出现许多问题。顺便说一下:后者在IE8中的瀑布图http://www.webpagetest.org/result/090825_24AP/没有显示并行加载,并且看起来像IE7http://www.webpagetest.org/result/090825_24AQ/。 - Arjan
@Bryan,我认为你的问题,尤其是那个IE8并行脚本加载,会让我在接下来的几周里睡不好觉...;-) 至于你的问题:请参见以下JS Bin,其中包含您的vAlign函数版本(稍作修改以允许使用提供的CSS),它确实显示出当CSS在JavaScript之后加载时$(this).height();可能会产生零,但在加载之前似乎很好。但仍然不知道为什么,恐怕每个浏览器的行为都可能不同:http://jsbin.com/ofola/edit - Arjan
2
浏览器异步下载样式表,但会阻止位于它们下方的脚本执行,直到样式表加载完成。他们猜测作者希望在后续脚本中查询样式信息,因此必须阻止执行以确保样式可用。 - sam
1
@sam 它实际上会阻塞,直到元素被渲染出来,这样js才能查询值,而不仅仅是等到样式表加载完毕。这被称为布局崩溃,是性能问题的常见来源。 - Juan Guerrero

15

对于我来说,CSS/JavaScript/JQuery的排序无效,但以下内容有效:

$(window).load(function() { $('#abc')...} );

同样的问题。我在IE9中遇到了js在样式渲染之前运行的问题,但是用这个替换$(document).ready解决了我的问题。很奇怪。无论如何还是谢谢 :) - Pure Function
即使是jQuery也这样写道:“虽然JavaScript提供了加载事件来执行在页面渲染时需要的代码,但是该事件直到所有资源(如图像)完全接收后才会被触发。”http://api.jquery.com/ready/,因此我也将尝试使用它来解决。 - Dariux
我进行了测试,CSS/JS重排序对我没有起到修复作用,但在加载外部样式表时,这种方法在Opera和Chrome中都有效。 - csga5000
2
这是一种终极选择。加载事件通常比dom-ready晚得多 - 只需考虑重型图像资产、Facebook iframes等的影响即可。如果脚本延迟到加载事件之后,可能需要多秒才能执行。 - hashchange

6

当所有DOM节点都可用时,DOM准备就绪事件就会触发。它与CSS无关。尝试在样式之前定位或以不同方式加载样式。


4
根据HTML5,DOMContentLoaded是一个普通的DOM就绪事件,不考虑样式表。然而,HTML5解析算法要求浏览器推迟脚本执行,直到所有前面的样式表都被加载完毕。(DOMContentLoaded和样式表
molily's tests (2010)中:
- IE和Firefox会阻止所有后续脚本执行,直到样式表加载完成。 - Webkit仅对外部脚本(<script src>)阻止后续执行。 - Opera不会阻止任何脚本的后续执行。 现代浏览器都支持DOMContentLoaded(2017),因此它们可能已经标准化了这种行为。

4
据我所知,当DOM加载完成时,ready事件会被触发 - 这意味着所有阻塞请求(例如JS)已经加载完毕,并且DOM树已经完全绘制。在IE中,ready状态依赖于一个较慢的事件触发器(document.readyState改变与DOMContentLoaded),因此时间取决于浏览器。
非阻塞请求(如CSS和图像)的存在完全是异步的,与ready状态无关。如果您需要这些资源,则需要依赖于传统的onload事件。

今天我在做一些测试时发现,某种方式下载CSS可能会阻塞(对于执行一些下一个<script>标签而言,而不是对于触发ready函数本身)。但如果这是downvote的原因,那么我也很想看到一些解释。这样就可以避免我进行一些测试了... - Arjan
我不是给你点踩的人,但你可能想看一下这个链接:http://stevesouders.com/cuzillion/?ex=2&title=Stylesheets+Block+Downloads - strager
CSS下载是非阻塞的,但它们可能会阻止后续脚本的执行。 - sam
以上链接肯定是表达 iframe 脚本可能会被父级样式表阻止的最令人困惑的方式。难道我理解错了吗?说真的! - Ryan Taylor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接