编写高效的 CSS 选择器
高效的CSS已经不是一个新的话题了,也不是我一个非得重拾的话题,但它却是我在Sky公司工作之时,所感兴趣的,关注已久的话题。
有很多人都忘记了,或在简单的说没有意识到,CSS在我们手中,既能很高效,也可以变得很低能。这很容易被忘记,尤其是当你意识到你会的太少,CSS代码效率很低的时候。
下面的规则只真正被应用到那些速度要求很高,有成百上千的DOM元素被绘制在页面上的大型网站。但是,实践出真理,这和你是在创建下一个Facebook,还是写一个本地的展示页面都没有关系,多知道一点总是好的。
CSS选择器:
对我们大多数人来说,CSS选择器并不陌生。最基本的选择器是元素选择器(比如div),ID选择器(比如#header)还有类选择器(比如.tweet)。
一些的不常见的选择器包括伪类选择器(:hover),很多复杂的CSS3和正则选择器,比如:first-child,class ^= “grid-”.
CSS选择器具有高效的继承性,引用Steve Souders的话, CSS选择器效率从高到低的排序如下:
- ID选择器 比如#header
- 类选择器 比如.promo
- 元素选择器 比如 div
- 兄弟选择器 比如 h2 + p
- 子选择器 比如 li > ul
- 后代选择器 比如 ul a 7. 通用选择器 比如 *
- 属性选择器 比如 type = “text”
- 伪类/伪元素选择器 比如 a:hover
以上引用自Steve Souders的Even Faster网站、
我们不得不提的是,纵使ID选择器很快、高效,但是它也仅仅如此。从Steve Souders的CSS Test我们可以看出ID选择器和类选择器在速度上的差异很小很小。
在Windows系统上的Firefox 6上,我测得了一个简单类选择器的(reflow figure)重绘速度为10.9ms,而ID选择器为12.5ms,所以事实上ID比类选择器重绘要慢一点点。
ID选择器和类选择器在速度上的差异基本上没有关系。
在一个标签选择器(a)的测试上显示,它比类或ID选择器的速度慢了很多。在一个嵌套很深的后代选择器的测试上,显示数据为440左右!从这里我们可以看出ID/类选择器 和 元素/后代选择器中间的差异较大,但是相互之间的差异较小。
注意: 这些数据可能在不同计算机和浏览器中间的差异较大。强烈地建议大家在自己的机子上测试一下。
组合选择器
你可以有一个标准的选择器比如 #nav,来选择任何带有ID为”nav”的元素,或在你可以有一个组合选择器比如#nav a,来选择任何在ID为’nav’的元素里面的链接元素
此刻,我们读这些是从左到右的方式。我们是先找到#nav,然后从它的里面找其他元素。但是浏览器解析这些不是这样的:浏览器解析选择器是从右到左的方式。
在我们看来,#nav里面带了一个a,浏览器却是看到的a在#nav里面。这些细微的差异对选择器的效率有很大的影响,同时学这些差异也是很有价值的。
如果想要知道更多浏览器这样解析的原因,请看Stack Overflow上的讨论
浏览器从最右边的元素开始(它想要渲染的元素),然后用它的方式回溯DOM树比从DOM树的最高层开始选择向下寻找,甚至可能达不到最右边的选择器—关键的选择器要高效。
这些对CSS选择器的效率有很大的影响。
关键选择器
关键选择器,正如前面讨论的一样,是一个复杂的CSS选择器中最右边部分。它是浏览器最先寻找的。
现在我们回到讨论开始的地方,哪类选择器是最高效的?哪个是会影响选择器效率的关键选择器;写CSS代码的时候,关键选择器是能否高效的决定因素。 一个关键CSS选择器像这样:
#content .intro {..}
是不是高效选择器比如类选择器天生就高效?浏览器会寻找.intro的实例(可能会很多),然后沿着DOM树向上查找,确定刚才找到的实例是否在一个带有ID为”content”的容器里面。
但是,下面的选择器就表现的不是那么好了:
#content * {..}
这个选择器所做的是选择所有在页面上的单个元素(是每个单个的元素),然后去看看它们是否有一个 #content 的父元素。这是一个非常不高效选择器因为它的关键选择器执行开销太大了。
运用这些知识我们就可以在分类和选择元素的时候做出更好的选择。
假设你有一个复杂的页面,它相当巨大并且在你的一个很大很大的站点上。在那个页面上有成百上千甚至上万的 a 标签。它还有一个小的社交链接区域放在一个ID为#social的Ul里面。我们假设它们是Twitter,Facebook,Dribbble还有 Google+的链接吧。在这个页面上我们有四个社交链接和成百上千的其他链接。 下面的这个选择器就自然的不是那么高效和合理了:
#social a {…}
这里发生的情况是浏览器会在定位到#social区域下的四个链接之前得到页面上所有成千上万的链接。我们的关键选择器匹配了太多我们不感兴趣的其他元素。
为了补救我们可以给每个在社交链接区域的 a 增加一个更特殊、明确的选择器 .social-link , 但是这好像有点违背我们的认知:当我们能用组合选择器的时候就不要放不必要的类标示在元素上。
这就是为什么我对选择器的性能如此感兴趣的原因了:必须在web 标准最佳实践和速度之间的保持平衡。
通常我们有:
<ul id="social">
<li><a href="#" class="twitter">Twitter</a></li>
<li><a href="#" class="facebook">Facebook</a></li>
<li><a href="#" class="dribble">Dribbble</a></li>
<li><a href="#" class="gplus">Google+</a></li>
</ul>
#social a {}
我们现在最好有:
<ul id="social">
<li><a href="#" class="social-link twitter">Twitter</a></li>
<li><a href="#" class="social-link facebook">Facebook</a></li>
<li><a href="#" class="social-link dribble">Dribbble</a></li>
<li><a href="#" class="social-link gplus">Google+</a></li>
</ul>
加上CSS:
#social .social-link {}
这个新的关键选择器将会匹配更少的元素,这意味着浏览器能够很快的找到它们并渲染特定的样式,然后专注于下一件事。
另外,事实上我们可以用.social-link{}更清晰的选择,而不是过分限制它。阅读下一部分你会原因…
简单的重述一次,你的关键选择器会决定浏览器的工作量,因此,我们应该重视一下关键选择器
过度限制选择器
现在我们知道了什么是关键选择器,还有它是大部分工作的来源,但是我们可以更乐观一点。拥有一个明确的关键选择器最大的好处就是你可以避免使用过度限制选择器。一个过度限制选择器可能像:
html body .wrapper #content a {}
这里的写的太多了,至少3个选择器是完全不需要的。它可以最多像这个样子:
#content a {}
这会发生什么呢? 首先第一个意味着浏览器不得不寻找所有的 a 元素,然后检查他们是否在一个ID为”content”的元素中,然后如此循环直到HTML标签。这样造成了太多的我们不太想要的花费。了解了这个,我们得到一些更现实的例子:
#nav li a{}
变成这个:
#nav a {}
我们知道如果a在li里面,它也必定在#nav里面,所有我们可以马上把li从选择器组中拿掉。然后,既然我们知道在页面中只有一个ID为nav的元素,那么它依附的元素就是完全没有关系得了,我们也可以拿掉ul
过度限制选择器使浏览器工作比它实际需要的更繁重,花费的时间更多。我们可以删掉不必需的限制,来使我们的选择器更简单和高效。
这些真的需要吗?
最短的答案是:或许不是。
最长的答案是:它取决于你正在搭建的站点。如果你正在为你的晋升而努力,那么就好好写出简单、高效的CSS代码吧,因为你可能不会感觉到它给你带来的改变。 如果你正在搭建下一个每个页面都以毫秒计算的Amazon网站,这样有时速度会很快,但有时可能不是。
浏览器将会在解析CSS的速度上变得更好,甚至在手机端。在一个网站上,你不太可能会觉察到一个低效的CSS选择器,但是….
但是
它确实发生了,浏览器还是不得不去做我们讨论的所有工作,无论它们变得多快。即使你不需要或者甚至不想实践任何一个,但是它都是我们值得学习的知识。请记住选择器可能会让你付出很大代价,你应该避免盯着一个看。这意味着如果你发现你自己在写像这样的:
div:nth-of-type(3) ul:last-child li:nth-of-type(odd) *{ font-weight:bold }
这时,你可能就做错了。
现在,在高效选择器的世界我还是一个新人。所以如果我忘记了什么,或者你有需要补充的,请在评论里面留言。
更多高效选择器
我还不能完全介绍Steve Souders的网站和书籍(《更快速网站》、《高性能网站》),它们是如此之好,以至于值得你花更多时间来阅读和推荐。这个家伙只有他自己才了解自己!
英文原文:Writing efficient CSS selectors,编译:@freestyle21 和@沈涛-WEB工程师
译文链接:http://blog.jobbole.com/35339/
编写高效的 CSS 选择器的更多相关文章
- 编写高效的CSS选择器
高效的CSS已经不是一个新话题,也不是一个我非得重拾的话题,但是,它却是自我在SKY工作以后,真正感兴趣并始终关注的一个话题. 很多人或者忘记了,或者仅仅是没有意识到,CSS可以是高效的也可能导致低能 ...
- [转] 编写高效的 CSS 选择器
高效的CSS已经不是一个新的话题了,也不是我一个非得重拾的话题,但它却是我在Sky公司工作之时,所感兴趣的,关注已久的话题. 有很多人都忘记了,或在简单的说没有意识到,CSS在我们手中,既能很高效,也 ...
- 编写高效的CSS选择符(节选)
最右边优先 css选择符是从右向左进行匹配的. 样式系统从最右边的选择符开始向左匹配规则.只要当前的选择符的左边还有其他选择符,样式系统就会继续向左移动,直到找到和匹配的元素,或者因为不匹配而退出. ...
- 网站CSS选择器性能讨论
CSS选择符由一些初始化参数组成,这些参数指明了要应用这个CSS规则的页面元素.作为一个网站的前端开发工程师,应该避免编写一些常见的开销很大的CSS选择符模式,尽量编写高效的CSS选择符,从而加快页面 ...
- CSS选择器性能分析
写了几篇关于js的博客,也是关于性能的,现在,我觉得有必要那css来认真分析一下了.之前只是看别人这么写就跟着写,但是没有去研究这样写或者是不是正确的写法,性价比怎么样,渲染的效率好么!这些都没有考虑 ...
- CSS选择器的优化
前面花了几个篇幅着重介绍了CSS的选择器的使用,我将其分成三个部分:CSS基本选择器.CSS属性选择器以及CSS伪类选择器.那么今天我主要想和大家一起来学习——CSS选择器方面的性能优化.因为对性能这 ...
- 书写更加高效的CSS,走出误区
根据一些CSS写作经验,如何提高渲染效率及所占用消耗的资源,我们来浅谈一下CSS的渲染效率,书写高效的CSS. 1.十六进制的颜色值对位数与大小写 编写十六进制颜色值时你可能会用小写字母或省略成3位数 ...
- 『心善渊』Selenium3.0基础 — 8、使用CSS选择器定位元素
目录 1.CSS选择器介绍 2.CSS选择器定位语法 3.Selenium中使用CSS选择器定位元素 (1)通过属性定位元素 (2)通过标签定位元素 (3)通过层级关系定位元素 (4)通过索引定位元素 ...
- CSS系列:CSS选择器
选择器(selector)是CSS中很重要的概念,所有HTML语言中的标记样式都是通过不同的CSS选择器来控制的.用户只需要通过选择对不同的HTML标签进行选择,并赋予各种样式声明,即可实现各种效果. ...
随机推荐
- Scrapy实战篇(七)之爬取爱基金网站基金业绩数据
本篇我们以scrapy+selelum的方式来爬取爱基金网站(http://fund.10jqka.com.cn/datacenter/jz/)的基金业绩数据. 思路:我们以http://fund.1 ...
- 序列化模块_pickle
序列化: 把不能够直接存储的数据变成字节流(bytes)保存在文件, 进行持久化存储 反序列化: 任何数据都可以转成字节流(bytes)进行存储: 1. dumps 把任意对象序列化 li = [1, ...
- [Notepad++]Notepad++怎么实现双视图/双窗口?
作为windows下非常优秀的开源代码编辑器,Notepad++是工程师必备代码编辑器.相比较之下,老一辈文本编辑器如ultraedit,editplus,就显得繁琐.冗余.疲惫.我使用过Notepa ...
- T4代码生成脚本从添加注释,添加命名空间开始(一款强大的代码生成工具从看懂脚本,到随心所欲的玩弄代码,本文只是T4的冰山一角,博主将不断深入探索并完善该文章)
精通T4脚本要从读懂脚本的关键代码片段开始 1.1. 给类添加注释-->看懂类对应的代码:<#=codeStringGenerator.EntityClassOpening(entity ...
- Anatomy of a Database System学习笔记 - 事务:并发控制与恢复
这一章看起来是讲存储引擎的.作者抱怨数据库被黑为“monolithic”.不可拆分为可复用的组件:但是实际上除了事务存储引擎管理模块,其他模块入解析器.重写引擎.优化器.执行器.访问方式都是代码相对独 ...
- 学习Flask框架
# -*- encoding: utf-8 -*- #导包 from flask import Flask #建立flask对象 app = Flask(__name__) #使用flask路由器 ...
- 初次安装Ubuntu后的若干配置步骤
第一步,安装VMware-tools 按照如下提示安装 首先将压缩包,复制到/home 目录下,将其解压,执行./vmware-install.pl即可 第二步,配置使linux能上网 首先将ubun ...
- mybatis学习 -每天一记 mybatis insert null 报错
mybatis 插入数据,model的属性存在null,插入报错 在使用mybatis 进行insert时,如果字段值存在null的情况,会出现插入失败的情况,解决方案: 如果使用spring boo ...
- C++_数字时钟软件实现设计
利用C++学习内容,通过windows自带函数实现一个简易的时钟 #include<iostream> #include<windows.h> //延时与清屏头文件 using ...
- c# statusStrip控件(转)
先了解一下StatusStrip:首选StatusStrip是Form中的一个控件,同时也是一个大的控件,其中含有许多子控件,这些子控件存放在控件群中. 这样我们要使用StatusStrip时, 首先 ...