在尝试减小网页的 HTML 大小时,我遇到了 Google 和 PageSpeed Firefox Add-On 的建议,它们提高了 CSS 选择器的效率,这(几乎)让我重新考虑了这些更改:
http://code.google.com/intl/de-DE/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors
具体来说,后代选择器非常适合使用 ID 或 CLASS 属性选择整个块(例如 DIV),然后使其所有子元素都没有 CLASS/ID 属性。但如果应用规则的遍历顺序如谷歌所述,则不应使用它们:
后代选择器效率低下,因为对于每个匹配键的元素,浏览器还必须遍历 DOM 树,评估每个祖先元素,直到找到匹配项或到达根元素。密钥越不具体,需要评估的节点数量就越多。
我非常怀疑浏览器是否使用了如此低效的遍历顺序,当然它们只会处理与顶部选择器组件匹配的元素的子树,即#foo span {...}
仅应检查 #foo 以下的元素,而不是每个跨度。查看最近浏览器代码的任何人都可以确认/否认这一点吗?
第二个有问题的建议是关于过度限定的选择器:
ID 选择器根据定义是唯一的。包含标签或类限定符只会添加需要进行不必要评估的冗余信息。
如果 ID 选择器根据定义是唯一的,为什么浏览器需要检查冗余信息?我知道他们这样做是因为例如,
div#foo { 颜色:黑色;} #foo { 颜色:白色;}
会导致 a 中出现黑色文本<div id=foo>
,但是 a) 它不应该完成(?需要 W3C 参考)和 b) 我不明白为什么当它导致对元素的简单 O(1) 检查时它会明显变慢标签名。
任何与现代浏览器源代码关系良好的人都可以阐明这些说法吗?由于大多数现代网站都使用后代选择器(包括 SO)并且它们具有明显的优势,所以我非常想使用它们......
编辑:
我对生成的页面进行了一些实验,似乎浏览器对后代选择器的处理确实很可怜:
包含(缩写)的页面:
#top a {文本装饰:无;}
#foo1 a.foo {颜色:红色;}
#foo2 a.foo {颜色:红色;}
[...重复 10000 次]
<主体标识=顶部>
<div>...[嵌套 50 次]<a href=foo>bla</a></div>[...]
[上一行重复 10000 次]
(基本上 10000 行,每行有 50 个嵌套的 div,要遍历到根节点和 1 个匹配的 10000 个选择器)
使用 Safari 5 在 2.2 秒内加载和呈现(直到window.onload()
执行的时间),使用 Firefox 3.6.10 只需不到 10 秒。
当.foo
从非应用规则中删除类选择器时,页面在 Safari 5 中大约需要 200 秒,在 Firefox 3.6.10 中大约需要 96 秒。这说明了后代选择器的实现有多么糟糕(在这种情况下,10000 条规则中的每一条都可能导致遍历到#top,在该规则失败的地方)。
儿童选择器的表现如何?#foo > span > div > div > div > div > div a {color: red;}
(也从不匹配,但强制遍历 6 个父节点)使用 Safari 5 需要 27 秒,使用 Firefox 3.6.10 需要 31 秒。
结论
后代选择器和子选择器目前在主流浏览器上都很糟糕。如果您关心速度,最好为所有样式标签添加丑陋的 class/id 属性,至少对于非常常见的 HTML 标签(如 a、img、div 等)。