打印

RS3005: IE6 IE7(Q) IE8(Q) 中 A 元素的 :visited :hover :active 伪类未按规范要求的算法来计算针对性

作者:丁宗秋 孙东国

标准参考

关于 A 标签的说明

  • A 标签是 HTML 中常见的标签之一,它有两个用途:
    1. 作为锚点:定义页面上的一个锚点,可通过其它 A 标签的 'href' 属性访问。
    2. 作为链接:将页面导航到该标签 'href' 属性所指定的链接资源地址(URI)。
  • A 标签有四个常用的伪类:
    1. ':link':适用于未被用户访问过的链接。
    2. ':visited':适用于已被用户访问过的链接。
    3. ':hover':在可视化客户端上,适用于光标(鼠标指针)指向一个元素,但还未激活它时。
    4. ':active':适用于一个元素被用户激活时。

    CSS 2.1 规范建议,在 A 标签上使用这四个伪类时,声明顺序应为:L-V-H-A。因为只有将 a:hover 放置在 a:link 和 a:visited 之后,才能确保在用户将光标指向 A 元素时,a:hover 内的声明能够覆盖之前 a:link 或 a:visited 中特性名相同的声明。同理,a:active 也应放置在 a:hover 之后,否则 a:active 中特性名相同的声明将被覆盖。

  • 当 A 没有 'href' 属性时,在 IE6 IE7(Q) IE8(Q) 中的 :hover 伪类以及 IE6 IE7 IE8(Q) 中的 :active 伪类会失效,详细信息请参考“参见”中的内容。

关于 A 元素的更多信息,请参考 HTML 4.01 规范 12.2 The A element 中的内容。

关于链接伪类的更多信息,请参考 CSS 2.1 规范 5.11.2 The link pseudo-classes: :link and :visited 中的内容。

关于动态伪类的更多信息,请参考 CSS 2.1 规范 5.11.3 The dynamic pseudo-classes: :hover, :active, and :focus 中的内容。

关于针对性的说明

根据 CSS 2.1 规范的描述,选择器有其针对性(specificity),可以应用到某一元素的多个规则集中,选择器的针对性越高,该规则集的权重也就越高。针对性相同的,后出现的规则集的权重更高。

针对性由 a b c d 四组数字组成,按照以下的方式计算:

  • 如果样式是在 HTML 代码中以 'style=...' 的内联样式的方式设置的,则将 a 组记为 1,b c d 三组均记为 0,否则 a 组为 0。
  • 将选择器中 ID 属性的数量总合计入 b 组。
  • 将选择器中其他属性及伪类的数量总合计入 c 组。
  • 将选择器中元素名及伪元素的数量总合计入 d 组。

确定针对性的强弱时,根据各组的数字来计算。a 组数字大的针对性更强,当 a 组的数字相同时,比较 b 组数字的大小,以此类推,最终比较结果更大的针对性更强。举例如:

* {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */ li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1
                */ li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */ ul li {} /* a=0 b=0 c=0 d=2 ->
                specificity = 0,0,0,2 */ ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */ h1 *[id=ok] {} /*
                a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */ ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3
                */ li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */ #xyz {} /* a=0 b=1 c=0 d=0 ->
                specificity = 0,1,0,0 */ style="..." /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

以上各组选择器,最后一行的内联样式针对性最强,倒数第二行有 ID 选择符的次之,往上的每一行依次减弱,最顶部的通配符针对性最弱。

关于规则集的更多信息,请参考 CSS 2.1 规范 4.1.7 Rule sets, declaration blocks, and selectors 中的内容。

关于针对性的更多信息,请参考 CSS 2.1 规范 6.4.3 Calculating a selector's specificity 中的内容。

问题描述

在 IE6 IE7(Q) IE8(Q) 中,A 标签的 :visited :hover :active 伪类声明即便没有以 L-V-H-A 的顺序书写,包含这些选择器的规则集中的、相同特性名的声明都会在相对应的行为发生时生效。而其他浏览器则严格按照规范规定的计算方式来确定选择器的针对性及它们的权重。

造成的影响

该问题将造成链接的样式在不同的行为发生时,在各浏览器中的表现不一致。

受影响的浏览器

IE6 IE7(Q) IE8(Q)

问题分析

1. 在 IE6 IE7(Q) IE8(Q) 中,a:hover、a:active 和 a:visited 并没有按照规范描述的算法来计算它们的针对性,而是根据链接的实际状态来决定使用哪个规则集里的声明。它们三个的针对性比 a:link 强。

根据规范规定,A 标签的四个常用伪类在计算针对性时的结果是相同的,因此它们的声明顺序应为:L-V-H-A,以确保各规则集中定义的相同特性名的声明可以按照期望的效果覆盖。

分析以下代码:

<html> <head> <style type="text/css"> a {font:bold 50px Verdana;} a:active
                {color:yellow;} /* [0,0,1,1] */ a:hover {color:blue;} /* [0,0,1,1] */ a:visited {color:green;} /*
                [0,0,1,1] */ a:link {color:red;} /* [0,0,1,1] */ </style> </head> <body> <div
                id="b1" class="c1"> <a class="c2" href="#">text</a> </div> </body>
                </html>
  • 代码将包含上述四个伪类的规则集的声明顺序写成 A-H-V-L,由于这四个规则集的选择器的针对性相同,因此它们中最后出现的权重最高,即无论页面中链接的状态为 :hover 或 :active,只要它已被访问过,它就会应用 a:visited 中的 'color:green'(绿色),而未被访问过的情况下,将应用 a:link 中的 'color:red'(红色)。

以上代码中的链接文字在各浏览器的不同状态下的颜色:

链接状态 IE6 IE7(Q) IE8(Q) 其他浏览器
链接未被访问过(a:link) 鼠标未放到链接上 red red
链接未被访问过(a:link) 鼠标指向链接(a:hover) blue red
链接未被访问过(a:link) 被激活(a:active) yellow red
链接已被访问过(a:visited) 鼠标未放到链接上 green green
链接已被访问过(a:visited) 鼠标指向链接(a:hover) green green
链接已被访问过(a:visited) 被激活(a:active) green green

可见,其他浏览器的表现都是正确的,但在 IE6 IE7(Q) IE8(Q) 中:

  • 当链接未被访问过(a:link)时,a:hover 和 a:active 在该链接相应的行为发生时,仍可生效。
  • 当链接已被访问过(a:visited)时,a:hover 和 a:active 在该链接相应的行为发生时,不能生效。

因此可以得出初步结论:a:hover 和 a:active 并没有按照规范描述的针对性算法来计算,它们的针对性比 a:link 强,但不比 a:visited 强。

那么是否 a:visited 比 a:hover 和 a:active 要高呢?调整以上代码的 CSS 部分的顺序后再次测试:

a {font:bold 50px Verdana;} a:visited {color:green;} /* [0,0,1,1] */ a:active {color:yellow;} /*
                [0,0,1,1] */ a:hover {color:blue;} /* [0,0,1,1] */ a:link {color:red;} /* [0,0,1,1] */
  • 将 a:visited 提前到第二行,测试 a:visited 与 a:active 及 a:hover 的针对性强弱差异。

链接文字在 IE6 IE7(Q) IE8(Q) 中的不同状态下的颜色(不再测试其他浏览器1):

链接状态 IE6 IE7(Q) IE8(Q)
链接未被访问过(a:link) 鼠标未放到链接上 red
链接未被访问过(a:link) 鼠标指向链接(a:hover) blue
链接未被访问过(a:link) 被激活(a:active) yellow
链接已被访问过(a:visited) 鼠标未放到链接上 green
链接已被访问过(a:visited) 鼠标指向链接(a:hover) blue
链接已被访问过(a:visited) 被激活(a:active) yellow

注 1:其他浏览器在这种情况下也有兼容性问题,但如果以规范建议的 L-V-H-A 的顺序声明规则集,则可以保证在其他浏览器中的表现一致,因此本文不在对这种情况下其他浏览器中出现的兼容性问题做深入分析。

可见,但在 IE6 IE7(Q) IE8(Q) 中:

  • 当链接已被访问过(a:visited)时,a:hover 和 a:active 在该链接相应的行为发生时,仍可生效。

再次交换 a:active 与 a:hover 的位置,测试结果相同。

结合以上的结论,可知:在 IE6 IE7(Q) IE8(Q) 中,a:hover、a:active 和 a:visited 并没有按照规范描述的算法来计算它们的针对性,而是根据链接的实际状态来决定使用哪个规则集里的声明。它们三个的针对性比 a:link 强。

2. 在 IE6 IE7(Q) IE8(Q) 中,a:hover、a:active 和 a:visited 的针对性比其他看起来针对性更强的选择器还要强。

事实上,在 IE6 IE7(Q) IE8(Q) 中,a:hover、a:active 和 a:visited 的针对性算法非常特殊,如以下 css 代码:

a {font:bold 50px Verdana;} a:visited {color:green;} /* [0,0,1,1] */ a:hover {color:blue;} /* [0,0,1,1]
                */ a:active {color:yellow;} /* [0,0,1,1] */ a:link {color:red;} /* [0,0,1,1] */ div a.c2 {color:black;}
                /* [0,0,1,2] */ div.c1 .c2{color:black;} /* [0,0,2,1] */

看起来最后一行规则集的选择器 'div.c1 .c2' 的针对性要更强,但实际并非如此,链接文字在 IE6 IE7(Q) IE8(Q) 中的不同状态下的颜色为:

链接状态 IE6 IE7(Q) IE8(Q)
链接未被访问过(a:link) 鼠标未放到链接上 black
链接未被访问过(a:link) 鼠标指向链接(a:hover) blue
链接未被访问过(a:link) 被激活(a:active) yellow
链接已被访问过(a:visited) 鼠标未放到链接上 green
链接已被访问过(a:visited) 鼠标指向链接(a:hover) blue
链接已被访问过(a:visited) 被激活(a:active) yellow

而将最后两行代码注释掉,换成以下代码再试:

a {font:bold 50px Verdana;} a:visited {color:green;} /* [0,0,1,1] */ a:hover {color:blue;} /* [0,0,1,1]
                */ a:active {color:yellow;} /* [0,0,1,1] */ a:link {color:red;} /* [0,0,1,1] */ /*div a.c2
                {color:black;}*/ /* [0,0,1,2] */ /*div.c1 .c2{color:black;}*/ /* [0,0,2,1] */ .c1 a.c2{color:black;} /*
                [0,0,2,1] */

结果如下:

链接状态 IE6 IE7(Q) IE8(Q)
链接未被访问过(a:link) 鼠标未放到链接上 black
链接未被访问过(a:link) 鼠标指向链接(a:hover) black
链接未被访问过(a:link) 被激活(a:active) black
链接已被访问过(a:visited) 鼠标未放到链接上 black
链接已被访问过(a:visited) 鼠标指向链接(a:hover) black
链接已被访问过(a:visited) 被激活(a:active) black

可以得出结论:针对性相同的选择器 'div.c1 .c2' 和 '.c1 a.c2'(针对性均为 [0, 0, 2, 1])在 IE6 IE7(Q) IE8(Q) 后者的针对性更强,包含 '.c1 a.c2' 的规则集的权重超过了包含 'a:hover'、'a:active' 和 'a:visited' 的规则集。

将 '.c1 a.c2' 换成针对性更强的 '#b1 a'(针对性为 [0, 1, 0, 1]),结果与上表相同。

解决方案

严格按照标准的建议,以 L-V-H-A 的顺序声明 A 标签的伪类,以保证在各浏览器中兼容。

当需要以另一组针对性更强的规则集覆盖之前定义的 L-V-H-A 规则集时,请以 L-V-H-A 的顺序重新定义一遍。如以下代码:

<!DOCTYPE HTML>1 <html> <head> <meta http-equiv="Content-Type"
                content="text/html; charset=utf-8" /> </head> <body> <style type="text/css"> a
                {font:bold 50px Verdana;} a:link {color:red;} /* [0,0,1,1] */ a:visited {color:green;} /* [0,0,1,1] */
                a:hover {color:blue;} /* [0,0,1,1] */ a:active {color:yellow;} /* [0,0,1,1] */ a.test {color:black;} /*
                [0,0,2,0] */ </style> <div> <a class="test" href="#">text</a> </div>
                </body> </html>

注 1:该代码声明的 DTD 触发所有浏览器的标准模式 (S),因此仅 IE6 受此问题影响。

本意是要 class 为 'test' 的 A 标签内的文字永远为黑色,但在 IE6 中将达不到目的。应将样式表部分修改为以下的形式:

a {font:bold 50px Verdana;} a:link {color:red;} /* [0,0,1,1] */ a:visited {color:green;} /* [0,0,1,1]
                */ a:hover {color:blue;} /* [0,0,1,1] */ a:active {color:yellow;} /* [0,0,1,1] */ a.test:link,
                a.test:visited, a.test:hover, a.test:active {color:black;} /* [0,0,2,1] */

以确保各浏览器中的效果一致。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6
Chrome 4.0.302.3 dev
Safari 4.0.4
测试页面: specificity.html
本文更新时间: 2010-08-07

关键字

a :link :visited :hover :active specificity L-V-H-A 链接 伪类 顺序 针对性 特异性 特殊性