玩转CSS选择器(二) 之 浏览器支持,常见Bug,性能优化

前言

上一篇系列文章整理了CSS选择器的基础使用方法,由于内容较多且细致,写了不少DEMO,目前将它整理成适合移动端浏览器的CSS选择器的参考手册,方便学习CSS的人参考使用,立刻就要搞定了,以后会放出 (笑脸)。javascript

本节内容会跟着上一节的内容继续完善,首先会补充CSS选择器的浏览器支持状况(主要是说IE),好比咱们最经常使用的s1,s2,…,sN群组选择器在IE7时才被支持,而且IE7还支持了不少咱们没有想到的选择器,如子元素选择器,属性选择器,了解后你会发现IE7仍是挺了不得的。css

以后还会补充日常使用选择器遇到的一些问题以及解决方案,最后了解浏览器是如何读取选择器的,怎样使用选择器能达到高效率。html

浏览器支持

了不得的IE7

当咱们在开发网页时,若是网页须要兼容IE6,那么天然地会把IE6和IE7浏览器归为一路货色,对于不兼容的选择器和属性都将再也不考虑使用,但是你是否知道IE7相比IE6增长了许多选择器能够用,如群组选择器,相邻选择器,兄弟选择器,属性选择器。java

如下选择器是不支持IE6,仅支持 IE7 及以上的浏览器web

基本选择器

选择器 描述 版本
s1,s2,...,sN 群组选择器,同时匹配全部s1元素或s2元素 2.1
E > F 子元素选择器,匹配全部E元素的子元素F 2.1
E + F 毗邻元素选择器,匹配全部紧随E元素以后的同级元素F 2.1
E ~ F 匹配任何E标签以后的同级F标签 3

属性选择器

选择器 描述 版本
E[attr] 匹配att属性的E元素 2.1
E[attr="val"] 匹配att属性且属性值等于val的E元素 2.1
E[attr~="val"] 匹配att属性且属性值中的词列表有一个等于val的E元素 2.1
E[attr^="val"] 匹配att属性且属性值为以val开头的字符串的E元素 3
E[attr$="val"] 匹配att属性且属性值为以val结尾的字符串的E元素 3
E[attr*="val"] 匹配att属性且属性值为包含val的字符串的E元素 3
E[att|="val"] 匹配att属性且属性值为以val开头并用链接符"-"分隔的字符串的E元素 2.1

IE7浏览器,单复选框的checked在属性选择器中是不被支持的,这部份内容会在下面的常见问题中详细说明。segmentfault

伪类选择器

选择器 描述 版本
E:hover 设置元素在其鼠标悬停时的样式 2.1
E:first-child 匹配父元素的第一个子元素E 2.1

E:hover在IE6中只有a元素可用浏览器

伪元素选择器

选择器 描述 版本
E:first-letter 选择文本块的第一个字母 2.1
E:first-line 选择元素的第一行 2.1

平庸的IE8浏览器

虽然来到IE8的时代,可是对于新选择器的支持并很少,不过还好咱们最经常使用的E:beforeE:after配合content属性都在IE8中获得了很好的支持。性能优化

如下选择器不支持IE6,IE7,仅支持 IE8 及以上的浏览器app

伪类选择器

选择器 描述 版本
E:focus 设置对象在成为输入焦点时的样式 2.1

伪元素选择器

选择器 描述 版本
E:before 在元素前面插入内容,配合"content"使用 2.1
E:after 在元素后面插入内容,配合"content"使用 2.1

狂拽炫酷*炸天的IE9

IE最好的时代就是迎接CSS3的到来,从IE9支持了一大坨新CSS3的伪类以及伪元素,我就勉强给IE使用上这个酷炫点的修饰语。工具

如下选择器不支持IE6,IE7,IE8,仅支持 IE9 及以上的浏览器

伪类选择器

选择器 描述 版本
E:checked 匹配用户界面上处于选中状态的元素E 3
E:enabled 匹配用户界面上处于可用状态的元素E 3
E:disabled 匹配用户界面上处于禁用状态的元素E 3
E:root 匹配文档的根元素,对于HTML文档,就是HTML元素 3
E:last-child 匹配父元素的最后一个子元素E 3
E:nth-last-child(n) 匹配父元素的倒数第n个子元素E 3
E:nth-of-type(n) 匹配同类型中的第n个同级兄弟元素E 3
E:nth-last-of-type(n) 匹配同类型中的倒数第n个同级兄弟元素E 3
E:first-of-type 匹配同类型中的第一个同级兄弟元素E 3
E:last-of-type 匹配同类型中的最后一个同级兄弟元素E 3
E:only-child 匹配父元素仅有的一个子元素E 3
E:only-of-type 匹配同类型中的惟一的一个同级兄弟元素E 3
E:empty 匹配没有任何子元素(包括text节点)的元素E 3
E:not(s) 匹配不含有s选择符的元素 3
E:target 匹配文档中特定"id"点击后的效果 3

伪元素选择器

选择器 描述 版本
E::first-letter 选择文本块的第一个字母 3
E::first-line 选择元素的第一行 3
E::before 在元素前面插入内容,配合"content"使用 3
E::after 在元素后面插入内容,配合"content"使用 3
E::selection 设置对象被选择时的样式 3

让IE6-8支持伪类和属性选择器

如何才能让IE6~8支持CSS3伪类和属性选择器,也许你已经想到了,咱们会用JavaScript工具来进行辅助,那么恰好|8e50989464f7517425e2c31ba2d6dd59424|就能够完成这件事情,并且使用起来很简单,只要把selectivizr.js引入到页面上就能够了,以下:

<!- -[if (gte IE 6)&(lte IE 8)]>

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

<![endif]- ->

可是使用它还有一些注意事项:

  1. 必需要引用一个JavaScript库,好比jQuery

  2. 只能解析<link>标签引入的样式,若是是<style>定义的样式是不会解析的

  3. 动态生成的DOM不会作二次映射

  4. 须要在标准模式的DTD才可以生效

项目地址:http://selectivizr.com

常见问题与Bug

* 通配符形成继承失效

* {
    color:red;
}

#test{
    color:blue; 
}
<div id='test'>
    <a href="#">text</a>
</div>

▲ 最终text的颜色倒是红色的

按照咱们的理解,id的优先级是高于*通配符的,而文字也本应该继承id元素的color值,因此最终的文字应该是蓝色呀。

因此这里混淆了一个概念,继承的样式的优先级永远低于元素自己的样式,包括通配符选择器,因此你们在开发中,应该尽量的避免滥用通配符,以避免带来一些隐性问题。

关于这个问题,还能够参考《关于CSS特殊性的问题

而在IE6及更早浏览器并不支持通配选择符(*),只是将它忽略了,因此也变相的能看到效果。

E:hover 失效

E:hover伪类用于设置元素在其鼠标悬停时的样式,可是在某种状况会致使效果失效,以下:

#test {
    background:red;
}
#test div { 
    display:none;
}
#test:hover div{
    display:block;
    background:yellow;
}
<div id="test">触发我<div>看到我了吧</div></div>

▲ 当触发#test:hover时,此效果是在IE6中是无效的,由于在IE6中,E:hover伪类仅能用于a(超连接)对象,且该a对象必需要拥有href属性。

E:hover还有一种失效的状态,是你们最多见的,代码以下:

a:link {color:gray;}
a:hover{color:green;}
a:visited{color:yellow;}
a:active{color:blue;}
<a href="#nogo">文字</a>

▲ 当超连接处于a:hover时,你会发现其效果是无效,文字的颜色不会变成绿色,这是由于超连接的伪类样式书写是有固定顺序的,不能颠倒,这四个属性正确的定义顺序为:

a:link {}
a:visited {}
a:hover {}
a:active {}

E:focus 失效

#test:focus + p {
    font-weight:bold;
}
<button id="test">点击我触发focus</button>
<p>文字</p>

▲ 当点击button按钮触发:focus时将邻近元素的文字进行加粗,可是这个效果在IE8是失效的,如何来修复它呢,只须要添加一个空的:focus选择器,以下:

#test:focus + p {
    font-weight:bold;
}
#test:focus {}

E:first-line 失效

若是在当前选择器内使用了!important:first-line伪类内部的定义的属性会被彻底忽略,示例:

p {
    color:blue;
}
p:first-line {
    color:red !important;
}
<p>第一行文字,<br/>第二行文字</p>

▲ 正常状况下第一行的文字会变成红色,可是在IE8浏览器却忽略它没有任何变化,如何来解决这个问题呢,把!important去掉就行了,以下:。

p {
    color:blue;
}
p:first-line {
    color:red;
}

E:first-letter 失效

E:first-letter失效和E:first-line失效的问题是相同的,解决方案请参考上方。

E > F 失效

就是子选择器中间有注释会致使属性失效,以下:

#test > 
/*子选择器*/
p {
    color:red;
}
<div id="test">
    <p>文字</p>
</div>

▲ 若是你非要这样写注释,那么在IE7下会致使子选择器失效,一样,E + F邻近选择器也有一样的问题,如何解决呢,不在选择器中间添加注释就能够了。

性能优化

CSS 选择器咱们都在使用,可是如何让它变的更简洁,高效呢?
首先选择器对性能的影响源于浏览器匹配选择器和文档元素时所消耗的时间,因此优化选择器的原则是应尽可能避免使用消耗更多匹配时间的选择器,可是在此以前咱们须要先了解浏览器的匹配机制,就是它是如何读取咱们的选择器的。

选择器匹配机制

#nav > a {
    color:red;
}

当咱们看到这个选择器的时候,会认为首先会找到id为nav的元素,而后在找到其子元素,将样式属性应用到a元素上。

事实上,却偏偏相反,由于浏览器读取选择器时,不是按照咱们的阅读习惯从左到右,而是遵循的从选择器的右边到左边进行读取的

当咱们知道这个匹配机制后,再回来看这个选择器,浏览器必须先遍历页面中全部的 a 元素,而后查找其父元素的id是否为nav,这样一来你就会发看似高效的选择器在实际中的匹配开销是很高的。

理解了CSS选择器从右到左匹配的机制后,咱们再看如下两种选择器:

div #nav
#nav div

你是否会认为第2种选择器的效率要高于第1种,那么就错了,其实第一个选择器的效率更高,由于第一个选择器的关键选择器使用了#id选择器”,而第二个选择器的关键选择器使用的是div标签选择器。

这里所说的关键选择器,就是CSS选择器中最右边部分,它是被浏览器最早寻找的,那么哪类选择器是最高效的?哪一个是会影响选择器效率的关键选择器?

选择器效率

在上面内容中咱们了解浏览器的匹配机制,以及关键选择器的重要性,那么哪些CSS选择器可以减小性能损耗呢?

Google 资深web开发工程师 Steve Souders 对 CSS 选择器的执行效率从高到低作了一个排序:

  1. id选择器(#id)

  2. 类选择器(.className)

  3. 标签选择器(div,h1,p)

  4. 相邻选择器(h1+p)

  5. 子选择器(ul > li)

  6. 后代选择器(li a)

  7. 通配符选择器(*)

  8. 属性选择器(a[rel="external"])

  9. 伪类选择器(a:hover,li:nth-child)

从Steve Souders的CSS Test咱们能够看出#id选择器和.className类选择器在速度上的差别很小很小。而在一个a标签选择器的测试上显示,它比#id选择器和类选择器的速度慢了不少,从这里咱们能够看出#id.className选择器 和 a标签、li a后代选择器中间的差别较大,可是相互之间的差别较小。

接下来举几个示例:

#nav {}
.menu{}
p#nav {}
p.menu {}

▲ 上面的选择器效率要高于下面的选择器,标签元素会下降选择器效率

优化建议

咱们理解了CSS选择器从右到左匹配的机制,也了解关键选择器的重要性,以及CSS选择器的效率排序,那么在使用选择器的时候,经过避免不恰当的使用,来提高 CSS 选择器性能。

避免使用通用选择器

#nav * {…}

▲ 这个选择器所作的是选择全部在页面上的单个元素(是每一个单个的元素),而后去看看它们是否有一个#nav的父元素。这是很是不高效选择器,开销太大了,应该避免关键选择器是通配选择器的状况。

避免使用标签或 class 选择器限制 id 选择器

/* Bad */
div#nav {…}
.menuBalck#menu {…}

/* Good */
#nav {…}
#menu {…}

▲ ID选择器自己就是惟一的,加上div反而增长没必要要的匹配;

避免使用标签限制 class 选择器

/* Bad */
span.red {…}

/* Good */
.text-red {…}

▲ 在标签上定义class选择器,在开发和维护时容易混淆,通常不建议这样写。

避免使用多层标签选择器。使用 class 选择器替换,减小css查找

/* Bad */
a[href="#"] > span > em {…}

/* Good */
.className {}

▲ 这种状况建议直接定义.className 选择器,而后使用<em class="className"></em>

避免过渡使用子选择器

/* Bad */
div ul li a {}
div > ul > li > a {}

/* Good */
.className {…}

▲ 这种状况建议直接定义.className 选择器,而后使用<a class="className"></a>

避免过分限制选择器

/* Bad */
html body .wrapper #content a {}

/* Good */
#content a {}

▲ 这里至少有3个选择器是彻底不须要的,过分限制选择器使浏览器工做比它实际须要的更繁重,花费的时间更多,因此这里应该避免。

利用可继承性

/* Bad */
#nav > li > a { color:red; } 

/* Good */
#nav { color:red; }

▲ 在使用选择器以前,请先考虑利用继承性实现

下一节继续整理选择器的优先级和继承性相关内容。

相关文章
相关标签/搜索