今天闲来无事,就用Firebug查看首页的HTTP瀑布图,测试页面加载时间,发现在外联的css和js上时间开销还是挺大的,虽然css和js都进行了gzip压缩,但是解析延迟在某些时候还是比较厉害,不是css解析延迟就是js解析延迟,在读取缓存的时候,也有一定的读取的时间开销。 鉴于这种情况,那么干脆就将首页的css和js都嵌入到页面中吧,把css放到head、js放到body闭合标签之前,反正这两个文件在gzip压缩的条件下也总共才三四K而已,而子页面里还是使用外联css和js的方式。 经过这样倒弄,首页加载速度平均有100ms的提升,欣喜若狂。更好笑的是yslow、page speed测试都得了满分,哈哈…… 让首页嵌入css和js,而子页面外联的方式,主要是出于这样的一个目的:用户第一次浏览到首页的时候,页面可以尽快的呈现,而到了子页面,就加载css和js,这样的用户体验就有了一个类似于“稳步退化”的感觉,使得用户不会在起先的时候就有个不良感觉,而之后的访问,子页面就可以充分的利用缓存,因为子页面可能包含图片、flash等等,缓解HTTP的并发数。 还有,之前在浏览器下可能会有短暂的无样式与渲染样式之前切换的闪白,目前也得到了比较好的缓解。
即时更新:由@drunber提供的解决办法:设置父元素的padding或者border、float、position:absolute也可以解决问题,经测试验证通过。但是有一点需要说明的是:上下padding和border是必须要有数值的,设置为0无效。 但是对于动画来说,overflow:hidden是必须要设置的,否则里面的文本不会被遮盖。 ———===========—– 即时更新分界线 ——===========———– 最近接到个需求,在搜索结果中添加下拉动画,高度和透明度都需要渐变。在测试过程中,遇到这样的一个问题:在一个div容器中包含了几个p标签,而且全在默认样式的情况,这样通过offsetHeight,clientWidth,scrollHeight等等获得div的高度,之后动态设置div的overflow样式属性为hidden,这样上面所获得的高度不准确了,《点击测试吧》,这样的后果就是动画被和谐了。 从这里的overflow:hidden对div的高度的差异可以看出:容器包含内容的解析原理。正常默认情况下,比如div包含一个p标签,这样div的宽高是会忽略p标签默认的margin值;但是一旦给div设置了overflow:hidden之后,div的实际宽高就需要加上p的margin值了。不过值得庆幸的是,在给div设置了overflow:hidden之后,通过offsetHeight等来获取div的宽高,是准确的。 从上面的分析中对于开头遇到的问题,就可以有三种方法来解决了:第一、在动态设置overflow:hidden之后来获取容器的高度,《点点测试吧》;第二、将p标签的margin清0处理,《再点点呗》;第三、干脆就不要使用p标签,改用没有默认margin的div吧,很勉强的方法,《最后点击测试下》。 Tip: 经过测试发现,设置overflow属性为hidden、auto都存在上面所说的情况。
CSS2.1是当前普遍使用着的CSS版本,平时如果循规蹈矩的编写CSS,或许不会发现问题,可问题就是:如果想要保存的时候,不小心按Ctrl+s的时候多留了一个s在CSS文件里,问题就开始来鸟;或者是不小心在规则的大括号外边多写了一个分号……出错的事情是千奇百怪的,对于新手来说频率可能会高些。出现这样的问题的时候、调试起来你可能会感觉很莫名其妙,这就需要了解一下CSS2.1版本的容错机制了。 CSS2.1的容错方式总的来说就是:对于出现一些无效的属性、属性值、@-keywords等等,它就会忽视这些样式声明或者整个规则声明。对于无效的属性、属性名会忽视该样式声明,对于@-keywords中keywords无效时,会将整个@-keywords包含的样式声明都忽略掉。 既然CSS2.1有这个容错机制,那么还有其他一些平时比较容易忽略的错误导致的样式失效的问题,下面举例出一些实例,看完下面的实例,基本可以了解CSS解析器解析CSS代码的一些原理了。 不要在CSS每个规则之间插入除空格之外的无效字符或者插入不合CSS语法的注释,否则该无效字符之后的第一个规则无效,例如: body{color:green;}. /*这里不小心多了个点号就杯具了*/ p.ten{color:red; background-color:green;} /*这个规则是无效的*/ p.one{color:red;} /*这个有效*/ 有一种情况比较特殊,当无效字符等使用大括号“{}”包含的时候,它下面的规则正常应用了,因为浏览器CSS解析器将它当作一条规则来处理。比如: body{color:green;} {.} p.ten{color:red; background-color:green;} /*它可以work啦*/ 跟上面对应的就是在样式中出现无效字符或者插入不合CSS语法的注释,则当前无效字符之后的第一条样式无效,例如: p.one{border-style:solid; . … 了解更多
2010-8-2 update:经过测试发现,但script的src和link的href属性值为#的时候,也会发生同样的事情:给服务器发送一个HTTP请求,并把当前页面作为返回值。浏览器的异同与下面的结论相同。通过js动态生成的script和link元素也一样。 ————-============————-============———— 有些时候,我们在写HTML页面的过程中,通过script标签来外联外部的js文件,可是不小心给src属性赋值为空,当然有时候也会在link元素中带上了空属性值的href属性,我们想当然的认为这个没多大的影响,浏览器也会按照你认为的方式来解析它们。但是这个想当然估计错误了。先看几个测试页面:《HTML5 Doctype下href=””》,《HTML5 Doctype下src=””》,《XHTML1.0 Doctype下href=””》。 使用firebug来查看DOM节点树的link和script节点,你就会发现怪异的事情发生了:link和script节点的内容居然是当前html页面的内容!!确实不可思议,就连容错机制最好的XHTML1.0的DTD声明也无济于事,这恐怕就是浏览器渲染页面的问题了。script节点src属性为空的时候就更恐怖了,会造成了语法错误。可更不可思议的是,出现这个问题的浏览器居然不是IE。经过多个浏览器的测试,IE和Opera都能很好的处理这个问题,可是Firefox、Chrome、Safari居然出现了。 对于这个初步总结为:当link标签的href属性为空、script标签的src属性为空的时候,浏览器渲染的时候会把当前页面的URL作为它们的属性值,从而把页面的内容加载进来作为它们的值。动态修改它们的href或者src属性值为空的时候,也将会出现这个问题:《动态修改href为空》。 因此,在我们平时写HTML页面,或者动态生成这两个标签的时候,尽量避免陷入了上面出现的陷阱,好好对待浏览器,编写符合规范的HTML标签语法和嵌套规则,多余的就别多此一举去倒弄,它回报给你的将是更少的开发时间和精力。 当某天你测试上面给出的测试样例并没有发现这个问题的时候,那就是浏览器修复了这个bug了。
2010-6-18 updates: surfing的时候发现了个非常不错的关于浏览器如何构建DOM树的工具:Live DOM Viewer。通过它可以非常直观的测试不同浏览器对解析HTML内容的差异。 2010-6-17 updates: 在跟番茄讨论这个问题的时候,他举例用CSS的样式来证明了body元素也会自动渲染的事实,这点本人没有想到,也没有经过测试就说了下面的文章(内容已经修改过了),惭愧……但是还存在这样的一个问题:就如下面的出现的情况,如果页面没有声明body标签的时候,为什么CSS可以渲染body的样式,而javascrpt无法直接调用document.body的引用?《测试用例》 经过一番测试和狂搜,大概明白了其中的原因,这需要了解javascrpt解析引擎和CSS渲染引擎是在什么时候介入页面起作用了。在浏览器解析HTML并生成DOM树的时候,是按从上至下的顺序解析的,浏览器最先会生成DOM树的根节点(HTMLDocument)和HTMLHEADElement(不知可否这样说),不管页面有没有声明html和head标签都会自动最先生成。之后再往下解析HTML标签,如果在遇到body标签(或者布局性标签)之前先遇到script标签的时候,就会在DOM树中生成该节点,但是这时这个script节点是head元素的子元素,而且这时候javascrpt解析引擎就会执行其中的代码(如果此时页面还没有解析到body的时候,或者还没有解析到如下面所说的布局性的标签的时候,DOM树中还不存在body节点,调用document.body就会出错),如果在body(或者其他布局性标签)之前,解析到link或者style标签,也会生成相应的节点,也是head的子节点,但是CSS渲染引擎并不会立即渲染页面的样式,它会在DOM树解析完成的时候才开始渲染页面,这就是上面的问题中所说的情况:CSS可以渲染body,而JS无法调用body的引用。《测试用例》,用firebug查看该实例生成的HTML结构。 所以,应该记住一点:如果在页面中使用script标签外联的js文件或者是行内脚本,当浏览器解析HTML内容的过程中遇到script标签的话,就会立即加载并且执行script的代码,之后才会继续解析后续的HTML的内容,除非是通过异步、动态加载js文件或者给script标签加上了defer、async属性。 但是如果在script或者link、style标签之前,有布局性的标签的时候,这就有两种情况了:没有文本内容的布局性标签和有文本内容的布局性标签。 当没有文本内容的时候。Chrome、Safari、Opera会将布局性标签下面的link、style、script等标签也渲染到body元素里(自动补全了该元素),这样通过script获取document.body的引用就没问题了。可是Firefox、IE会将link、style、script等标签都渲染到head元素里,先于body元素先在DOM树中生成,这样在js代码里直接获取document.body的引用就会出错。《测试用例》 当有文本内容的时候。各个浏览器都显示一致了。都会把link、style、script都渲染到body里,这样body元素就先于link、style、script标签等先在DOM树中生成,获取body的引用就没问题了。《测试用例》 ============================================================================ 正如题目中所说,一个正常的包含html、head、body的标签是不会出现这个问题的,页面都有声明了,浏览器它就省事多了。但是当页面没有html、head、body标签的时候,浏览器就有点发狂了,它惩罚偷懒的人的时候到咯。 在开始叙述之前,先来测试几个在不同的条件下的样例(这个测试主要是针对下面将要叙述的关于动态加载js来说的),下面将会提到一个暂时不知道怎么表达的词,就是我说的“布局性的标签”,它主要是指p、span、div等这些具有布局性质的元素,因此script、link、style等就不是了。 当页面无html、head、body标签,而且也没有其他的布局性的标签,只有一个script:《测试用例》。document.body出错了 当页面无html、head、body标签,有布局性的标签,而且带有文本内容(比如:<p>hello</p>,嵌套的空标签也不行),当这些标签在当前script标签前面的时候:《测试用例》。可以获得document.body的引用,它变好了。但是如果前面不是布局性的元素,或者是嵌套没有文本内容的布局性元素,《前面是link元素》,《嵌套空布局性元素》,在空布局性标签的情况下,IE、Firefox都挂了,chrome,safari,opera正常。前面是link元素的话全挂鸟。 当页面无html、head、body标签,有布局性的标签,而且不管有没有内容,当这些标签在当前script标签后面的时候:《测试用例》。document.body出错,它纠结了。 跟上面相同的条件,document.documentElement和document.getElementsByTagName(“head”)都可以获得引用:《documentElement》,《document.getElementsByTagName(“head”)》 … 了解更多
2010-6-28 updates 之前的测试都在页面中直接通过link元素外联css文件,但是动态插入的情况呢?(非常感谢Emu的提醒) 因此,又测试了使用DOM Element的方式来动态插入link元素,并外联css文件,可是问题依旧,《测试示例》。IE、Chrome、Safari照常需要等到全部的样式表都加载下来才开始呈现页面样式;FF、Opera是加载完一个样式表就渲染页面,而不需要等到全部的样式表都加载下来。 之后,测试使用一个定时器(setTimeout)的方式来异步执行动态加载css文件,《测试页面》,这下子就有点意思了:IE出现了无样式内容闪烁,仍然需要等全部的样式表都加载下来之后才呈现页面;Chrome、Safari跟FF、Opera一样了,加载完一个样式表就渲染页面,不需要等到全部的样式表都加载下来。 不过定时器的方式虽然解决了Chrome、Safari的问题,但是使得IE可能会出现无样式内容闪烁,所以也并不是很完美的方案,就看网站的用户群偏向哪些浏览器多一些了,并对这些浏览器做这方面的优化。 ——————— 完美的分界线 ———————- 一直存在一个疑问:IE同其他主流浏览器的兼容性差异都涉及到布局、HTML、Javascript、CSS规则等方面,但是对于解析样式表来渲染整个页面呢,是否也存在异同?当看到Steve Souders的博文《Frontend SPOF》,迷惑揭开了,文中对各种浏览器解析样式表的机制做了如下描述: Browsers are split on how they handle … 了解更多