张鑫旭-鑫空间-鑫生活
it's my whole life!备份内容浏览
CSS中:visited链接伪类的怪癖
肖继潮
在对锚点元素 a 设置样式时,一般我们都知道 Love or hate 规则,即必须按照如下的顺序写选择器:
a { ... }
a:link { ... }
a:visited { ... }
a:hover { ... }
a:active { ... }
背后的原理其实很简单,即按照CSS层叠规则,后出现的样式会覆盖先出现的样式。这里我就不多说了。
原本以为记住 Love or hate 规则就没啥问题了,今天被同事一问,才发现还有东西没搞清楚呢。
问题是这样的,假如有如下HTML代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
a {
font-size: 3em;
}
a:link {
color: red;
}
a:visited {
color: blue;
background-color: red;
font-size: 30px;
}
a:hover {
color:black;
background-color: gray;
font-size: 30px;
}
a:active {
color:orange;
background-color: blue;
font-size: 30px;
}
</style>
</head>
<body>
<a href="#">Hello</a>
</body>
</html>
意图是让访问过的链接背景色变为红色,字体大小变为30px。然而,打开浏览器预览,点击链接之后,结果却是如下这样子:
访问过的链接的背景色和字体大小设置都不起作用,而字体颜色设置却起作用了。
然而,如果在 a 或者 a:link规则中设置background-color,那么a:visited规则中设置的background-color就会起作用。不过,即使在 a 或者 a:link规则中设置了font-size,a:visited规则中的font-size也不会起作用。而对于a:hover和a:active规则却没有这些奇怪的现象。
带着疑问,打开了CSS3选择器规范:Selectors Level 3。有关链接伪类的也只有一小段:
其中有一段话引起了我们的注意:
注意:样式表的作者可能会在没有用户同意的情况下,滥用 :link 和 :visited 伪类,以判断用户访问过哪些网站。因此,用户代理可能会把所有链接当作未访问的链接,或者实现其它措施,以保护用户隐私,同时以不同方式渲染访问过的以及未访问过的链接。
难道这玩意还跟用户隐私保护有关系?可是规范也太简单了,根本没法理解。去 MDN 查一下,看有没有详细解释。在 MDN CSS参考指南中对 :visited 的解释中发现了一段话:
中文翻译过来就是:
注意:出于隐私的考虑,浏览器严格限制用此伪类选择的元素应用的样式:只能应用color, background-color, border-color, border-bottom-color, border-left-color, border-right-color, border-top-color, outline-color, column-rule-color, fill 和 stroke。还要注意,alpha 组件会被忽略,会用非visited规则的alpha组件替换(除了透明度为 0 时,此时整个颜色都被忽略,并用非 visited规则之一)。
虽然颜色可以被修改,不过方法 getComputedStyle 会撒谎,总是返回非 visited 颜色值。
前一句的意思还能明白,就是:a:visited 规则上只能设置上面指出的几种属性,font-size 不在其中,所以在 a:visited 规则上设置 font-size 是不起作用的。好了,这个明白了。
不过,明明是允许设置 background-color,为嘛一定要在 a 或者 a:link 上设置了 background-color 后,a:visited 上设置的 background-color 才会生效呢?是不是跟这个什么 alpha 组件有关系呢?
这个 alpha 组件是什么意思呢?我们知道CSS3中颜色可以用RGBA表示法。其中,R代表红色值,G代表绿色值,B代表蓝色值,而A代表alpha值。这个alpha值用来表示颜色的透明度,取值是0到1之间,0代表透明,1代表不透明。那么“alpha 组件会被忽略,会用非visited规则的alpha组件替换(除了透明度为 0 时,此时整个颜色都被忽略,并用非 visited规则之一)” 的意思就是:如果在:visited规则上设置的颜色透明度为0,即transparent,那么就会用未访问过的规则完全替换它;如果颜色透明度不为0,那么就会用未访问过的规则的颜色透明度来替换这个alpha值。
我们用代码来证明一下。设置四个链接,分别设置未访问过的链接的颜色透明度为0,0,1,1,对应的访问过的链接的颜色透明度0,1,0,1。完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
a:link.a1 {
background-color: rgba(0,1,0,0);
}
a:visited.a1 {
background-color: rgba(255,0,0,0);
}
a:link.a2 {
background-color: rgba(0,255,0,0);
}
a:visited.a2 {
background-color: rgba(255,0,0,1);
}
a:link.a3 {
background-color: rgba(0,255,0,0.2);
}
a:visited.a3 {
background-color: rgba(255,0,0,0);
}
a:link.a4 {
background-color: rgba(0,255,0,1);
}
a:visited.a4 {
background-color: rgba(255,0,0,1);
}
</style>
</head>
<body>
<a href="#" class="a1">Hello1</a>
<a href="#" class="a2">Hello2</a>
<a href="#" class="a3">Hello3</a>
<a href="#" class="a4">Hello4</a>
</body>
</html>
在浏览器中预览一下,点击各个链接一次,最后效果如下:
结果表明:
1)如果未访问过的链接的颜色是透明的,那么访问过的链接的颜色也一定是透明的,所以链接a1、a2的背景色是透明的。
2)如果未访问过的链接的颜色是不透明的,而访问过的链接的颜色是透明的,那么原本访问过的链接背景色是不会出来的,但是因为alpha值会被未访问过的链接的alpha值替换,所以背景色也出来了(注意:FireFox会用未访问过的链接的RGB完全替换访问过的链接的RGB,而Chrome、Safari依然采用访问过的链接的RGB)。
3)如果未访问过的和访问过的链接的颜色都是不透明的,那么采用的是访问过的链接的RGBA中alpha值会被未访问过的链接的RGBA的alpha值替换,RGB保持不变。
至此问题就都搞清楚了。
总结:
1)浏览器根据隐私策略,会限制:visited链接伪类样式规则中出现的属性,只有下列的属性才能被应用到已访问链接:
- color
- background-color
- border-color (及其子属性)
- outline-color
- fill 和 stroke 属性的颜色部分
2)如果要给已访问链接添加颜色属性,未访问链接必须添加对应的颜色属性,并且其透明度不能为0。而已访问链接的最终颜色,跟浏览器(FireFox与Chrome、Safari、IE有所不同)的处理方式有关,详见上述解释。
PS: 备份内容仅显示纯文字。