CSS Hack Table

This 当前浏览器 Y 渲染 N 不渲染 H 部分版本或部分属性渲染 B 样式失效
THIS   windows Mobile Linux Mac
IE Firefox Chrome Safari Opera Android iOS Firefox Chrome Opera Safari Firefox Chrome Opera
6 7 8 9 2 3 7 9 5 15 4 5 8 9 10 11 2.3 5.0 9 17 11 5 9 17 11
s1 * html #selector Y N N N N N N N N N N N N N N N N N N N N N N N N
s2 *+html #selector N Y N N N N N N N N N N N N N N N N N N N N N N N
s3 *:first-child+html #selector N Y N N N N N N N N N N N N N N N N N N N N N N N
s4 html>body #selector N Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
s5 html>/**/body #selector N N Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
s6 #selector:not(x) N N N Y Y Y Y Y Y Y Y Y N N Y Y Y Y Y Y Y Y Y Y Y
s7 body:last-child #selector N N N Y Y Y Y Y Y Y Y Y N N Y Y Y Y Y Y Y Y Y Y Y
s8 body:nth-of-type(1) #selector N N N Y N H Y Y Y Y Y Y N N Y Y Y Y Y Y Y Y Y Y Y
s9 body:first-of-type #selector N N N Y N H Y Y Y Y Y Y N N Y Y Y Y Y Y Y Y Y Y Y
s10 html[lang] #selector N Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
s11 :root *> #selector N N N Y Y Y Y Y Y Y Y Y N N Y Y Y Y Y Y Y Y Y Y Y
s12-1 #selector, x:-moz-any-link N Y N N Y Y Y Y N N N N N N N N N N Y N N N Y N N
s12-2 #selector, x:-webkit-any-link N Y N N N N N N Y Y Y Y N N N N Y Y N Y N Y N Y N
s13-1 #selector, x:-moz-any-link, x:default N Y N N N Y Y Y N N N N N N N N N N Y N N N Y N N
s13-2 #selector, x:-webkit-any-link, x:default N Y N N N N N N Y Y Y Y N N N N Y Y N Y N Y N Y N
s14 body:not(:-moz-handler-blocked) #selector N N N N N H Y Y N N N N N N N N N N Y N N N Y N N
s15 html:first-child #selector N N N N N N N N N N N N Y H N N N N N N N N N N N
s16 #selector,x:ie Y Y N N N N N N N N N N N N N N N N N N N N N N N
p1 property:value\9; Y Y Y Y N N N N N N N N B B N N N N N N N N N N N
p2 property:value!ie; Y Y N N N N N N N N N N N N N N N N N N N N N N N
p3 *property:value; Y Y N N N N N N N N N N N N N N N N N N N N N N N
p4 _property:value; Y N N N N N N N N N N N N N N N N N N N N N N N N
p5 property /**/:value; N Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
p6 property /*\**/:value\9; N Y Y Y N N N N N N N N B B N N N N N N N N N N N
p7 property: /*\**/value\9; N N Y Y N N N N N N N N B B N N N N N N N N N N N
p8 property:value\0/; N N Y H N N N N N N N N N N N N N N N N N N N N N
p9 property:value\0; N N Y Y Y N N N N N N N B B Y Y N N N N Y N N N Y
m1 @media screen and
(-webkit-min-device-pixel-ratio:0){...}
N N N N N N N N Y Y Y Y N Y N N Y Y N Y N Y N Y N
m2 @-moz-document url-prefix(){...} N N N N Y Y Y Y N N N N N N N N N N N N N N N N N
m3 @media screen\9{...} Y Y N N N N N N N N N N N N N N N N N N N N N N N
m4 @media \0screen\,screen\9{...} Y Y Y N N N N N N N N N N N N N N N N N N N N N N
m5 @media \0screen{...} N N Y N Y H N N N N N N N N N N N N N N N N N N N
m6 @media screen\0{...} N N Y Y Y H N N N N N N N Y Y Y N N N N Y N N N Y
THIS   6 7 8 9 2 3 7 9 5 15 4 5 8 9 10 11 2.3 5.0 9 17 11 5 9 17 11
IE Firefox Chrome Safari Opera Android iOS Firefox Chrome Opera Safari Chrome Firefox Opera
windows Mobile Linux Mac

如何使用

必须明确一点,我个人抵制任何Hack。之所以做出这张表,是出于对一个时期的缅怀,这同时也是我给IE6/7树立的一座墓碑。

把hack的种类分成三种的话,分别是选择器hack、属性hack和Query Hack。将它们分类标号,开头分别是s、p、m。点击This一列里的标号,就可以查看我对于这个hack的简单说明。

这里列举都是原子hack,即实际使用中,可以是一种或者任意数种的任意组合,以交集获得特定浏览器正确渲染——当然这也是极其糟糕的做法——就如同每一种武器都可以是凶器,组合几种就成了大杀器...决不放弃使用武力,因为往往总有些时候,不可避免。

尽管我花了大量的时间测试,但是一个人的精力毕竟有限,错误在所难免。如果你发现任何问题,请联系我 iifksp[at]swordair.com,非常感谢。

选择器Hack

选择器Hack主体式基于浏览器对选择器的支持度,当然也有很多bug被利用。

s1: * html #selector

Star HTML hack,仅IE6及以下支持渲染,所有其他浏览器均不支持,可能是最为常用也是最为滥用的hack写法。W3C标准规定 html 作为其DOM的根元素,但是从IE4开始直到IE6,IE的 html 之上还有一个以 * 选取的根元素。所以标准浏览器忽略此条规则,仅IE4-6匹配。这条hack能通过CSS验证,但因为使用了 * 通配符而为人诟病,虽然应用广泛但会造成一些不良影响。

s2: *+html #selector

IE7的出现虽然修复了IE6根节点选择的错误,但是同时也带来了新的问题。这条hack仅在IE7标准模式及IE8的兼容模式(IE7标准)中正常渲染,其他浏览器、IE8标准模式、IE7怪异模式均忽略。与s1相同,可以通过CSS验证。

s3: *:first-child+html #selector

作用和说明同s2。值得一提的是,经过我的测试,这条规则并非完全等值于s2,当使用IE9并调整到IE7标准模式的情况下,这条hack是无效——非滤镜的css在IE9的开发模式下失效并不常见。

s4: html>body #selector

从IE7开始才支持的子元素选择器(child selector)区分了IE7及之前版本,除了IE6-以外,所有浏览器正常渲染。子元素选择器将选择结果限定在了父节点的直接子元素,是因为IE6的存在而大范围使用受限的选择器之一。

s5: html>/**/body #selector

尽管如s4所述,IE7实现了子元素选择器,但是简单地在IE7子元素选择器的代码里添加空注释就能让IE7无法识别这条CSS规则,从而也将IE7排除在外。当前除了IE6与7,所有的浏览器都能正确渲染。

s6: #selector:not(x)

:not 是CSS3里的一个伪类,称为“否定伪类”,用于否定的描述性运算。hack里的 x 只是占位,其可以是任意支持的其它简单选择器。由于是使用CSS3的特性,所以IE8-、Opera9-均不支持。同理,其它CSS3伪类也可以用来写同类hack,诸如 :empty 等。

s7: body:last-child #selector

:last-child 以及 :first-child 都是因为浏览器的支持不够而使用受限的超级方便的伪类(我都不知道我不得已写了多少 class="last-item" 了),也正因为如此它也被用来hack。表现同s6相同——IE8及以下、Opera9及以下直接忽略规则。

s8: body:nth-of-type(1) #selector

:nth-of-type伪类用于公式化的选择,IE9、Safari 3+、chrome、opera 9+ 以及Firefox 3.5+支持。

s9: body:first-of-type #selector

识别度与s8相同,等价于s8。

s10: html[lang] #selector

[] 是属性选择器,本应被广泛使用,因为其在编写应对某些有特定属性的元素的样式时非常有用,仅IE6不支持。

s11: :root *> #selector

:root伪类选中所有块的根元素。虽然现代浏览器普遍支持此伪类,但IE直到9才开始支持,所以 IE8-忽略此条规则。

s12-1: #selector, x:-moz-any-link

由于带有 -moz 前缀,所以只匹配Firefox(1+)。这种规则可以在Firefox默认的UA样式里(us.css)找到:

*|*:-moz-any-link {
	cursor: pointer;
}

虽然每个版本不尽相同,但大都如上所示。webkit也有类似的 -webkit-any-link

IE7由于忽略x之后的值,所以也会诡异的匹配这条规则,可以通过添加s5的 html>/**/body 来过滤IE7。

s12-2: #selector, x:-webkit-any-link

s12-1的webkit版本,说明同s12-1。

s13-1: #selector, x:-moz-any-link, x:default

与s12-1类同,区别是Firefox从3.0才开始支持。

s13-2: #selector, x:-webkit-any-link, x:default

s13-1的webkit版本。

s14: body:not(:-moz-handler-blocked) #selector

-moz-handler-blocked 同样属于Mozilla的CSS 扩展,类似的属性可以在 Mozilla CSS Extensions 这个列表里找到,并都标明了所需Gecko版本。由于是专属,只有Firefix、Seamonkey、Thunderbird识别并正确渲染,其他浏览器则都会忽略。比如 -moz-handler-blocked 就需要 Firefox 3.5+。

s15: html:first-child #selector

:first-child只匹配当前元素是其父元素第一个子元素的情况。但Opera 9.27以及其之前的版本会选中根元素 html,即使它不是另一元素的子元素。 /* Opera 9.27 and below, safari 2 */

属性Hack

属性Hack里,出现的最多的就是反斜杠和注释。反斜杠是一个很特别的字符,其在属性里出现会被浏览器直接忽略(IE6+)。比如:

p\r\o\e\t\y:value;
property:value;

这两条规则无异,浏览器都能正确识别并渲染。

\0 更为特别,作为C语言的字符值null、字符串的结尾,被用在Hack上也就不足为奇了。这也是为什么属性Hack以及下面的查询Hack多次出现这些字符的原因。

p1: property:value\9;

最为常见的IE hack之一,IE直到IE9都仍然识别这种属性值。

p2: property:value!ie;

属性之后的叹号后面可以出现任何字符串,在IE7-作用与 !important 相同,IE8以上则会忽略。

p3: *property:value;

著名的“Star Hack”,IE7-识别在之前加星号的属性,所以这条经常被用来Hack轻微的IE7-的渲染不一致。其实 * 只是我们最为常见的符号hack,在这个hack里,* 可以被下面的任何一种字符代替,效果完全一致。

! $ & * ( ) = % + @ , . / ` [ ] # ~ ? : < > |

甚至是上述字符的任意组合,IE7-都会忽略属性之前的这些字符就好像它们不存在一样,而无论属性之前有多少个这些字符。因此,看起来像是注释的这种下面这些写法其实也是这种hack的变种:

//background:skyblue;
##background:skyblue;

p4: _property:value;

针对于IE6-的被广泛使用的下划线hack,常常和p3的星号hack同时出现,用来重写IE6-的样式。和p3一样,规则中的字符 _可以被替换成其它字符,并且任意替换和组合下面这些字符都是有效的:

_ - £ ¬ ¦

p5: property /**/:value;

除了IE6以外,所有浏览器都能正确识别注释。需要注意的是,这里的 property/**/之间的空格是必须的。如果没有空格,IE6也能正确识别。

p6: property /*\**/:value\9;

与p5不同的是,加入了 \9 hack之后,IE之外的浏览器全部忽略了这条规则。属性后的空格也是必须的。需要注意的是,此hack后的分号的有无对浏览器有影响:

另外,如果使用了无分号的Hack,则不能再在同一个定义里添加规则,需要分别重新定义。

p7: property: /*\**/value\9;

写法与p6只有细微差别,空格同样必须。区别是IE7不再能识别。

p8: property:value\0/;

只有IE8能够识别的hack规则IE8识别此规则,IE9部分识别 订正[1],当被放在同一个定义里时,其必须被放置在所有规则之后。

p9: property:value\0;

标准的 \0 hack,IE高版本(8+)、Firefox低版本(2-)以及Opera(部分,具体见表),都能识别并渲染。

Query Hack

这里主要指的是Media Query,关于Media Query 可以参考我写的 CSS3 Media Queries 详解。这里的hack也同样多是特殊字符的增插。

m1: @media screen and (-webkit-min-device-pixel-ratio:0){...}

所有的webkit均匹配这条规则(safari 3+, chrome 1+),包括移动版。但经过我的测试,Opera 9.0会非常诡异的匹配这条Media Query。所以虽然这条规则在网上被广泛使用,却并非完美。

m2: @-moz-document url-prefix(){...}

仅Firefox(1.5+) 所支持的一种的限定URL的@样式规则,关于其说明可以参考Mozilla Developer Network的 @-moz-document。是为数不多的针对Firefox的Media Query Hack。

m3: @media screen\9{...}

与这条规则神似的属性hack是p1,但IE的表现差别却很大。只有IE7及以下渲染这样的规则。

m4: @media \0screen\,screen\9{...}

与m3相比只是多了IE8的识别。

m5: @media \0screen{...}

只有IE8以及低版本Firefox识别并渲染。更为严格的3.6已经不再识别。

m6: @media screen\0{...}

更为标准的 \0 hack,相比m5多了IE9以及Opera的识别。可见 \0 仍然是神奇的。Opera的表现也与其在属性hack里的同类hack中保持一致,即如果 \0 出现在字符串值结束处,Opera就会识别这条规则。

参考资料:

  1. Browser CSS hacks by Paul Irish
  2. The two CSS Selector bugs in IE6 by Paul Irish
  3. In defense of CSS hacks — introducing “safe CSS hacks” by Mathias Bynens
  4. CSS hacks playground - the hunt for an IE8-only CSS hack by Mathias Bynens
  5. Personal CSS Hacks for IE6, IE7, IE8, IE9 by Dimox
  6. CSS filter: Will the browser apply the rule(s)? by KEVIN C SMITH
  7. CSS hacks by David Hammond
  8. CSS Hacks Targeting Firefox by Chris Coyier
  9. CSS filter by Wikipedia
  10. Improving the CSS 2.1 strict parser for IE 7 by IEBlog
  11. Selectors Level 3 by W3C
  12. @-moz-documentby Mozilla Developer Network
  13. CSS3 Media Queries 详解 by 葵中剑

感谢:

  1. 感谢 ATA 的 Neil,给了我第一份工作,以及学习的时间。
  2. 感谢我曾经的团队:阿里巴巴 AliExpress UED 的每一位,特别是团长、老赵、导师小运。
  3. 感谢 Ctrip English Website Department 在我最迷失的时候收留我,特别感谢面试我的 Allen 和 Brice。

订正信息:

  1. 感谢@Max_Lee提出IE9支持 property:value\0/; 的大部分属性

再见了,IE6/7,后会无期!——by 葵中剑 2012-02-02