CSS a:link与a:visited伪类链接样式未正常覆盖生效问题
你对:link和:visited两个伪类的基础定位理解没有错::link确实匹配未被用户访问过的链接,:visited匹配用户已访问过的链接。样式不生效的核心原因是现代浏览器为了防止恶意脚本嗅探用户的浏览历史、窃取隐私,对:visited伪类可生效的CSS属性做了极严格的限制。
目前所有主流浏览器仅允许给:visited状态设置和颜色相关的CSS属性,包括:
colorbackground-color- 各方向边框颜色(
border-*-color) outline-colorcolumn-rule-color- SVG填充/描边颜色(
fill/stroke)
这个限制是2010年前后各大浏览器针对历史嗅探攻击统一上线的安全策略:早期浏览器没有相关限制时,恶意站点可以批量生成上千个链接、给已访问状态设置特殊样式,再通过JS读取元素样式特征,就能在用户无感知的情况下枚举判断用户访问过哪些站点,窃取隐私。
除此之外所有会改变元素尺寸、布局、视觉权重的属性都会被浏览器直接忽略,不会生效,你代码中用到的font-weight就在被限制的属性列表里,所以写在:visited里的font-weight: normal根本不会被应用,所有链接都会始终应用:link中设置的粗体样式,和你的预期不符。
额外说明:哪怕是上述允许设置的颜色属性,浏览器也做了两层限制:一是不支持设置透明值、不支持调整透明度通道;二是JS通过getComputedStyle读取样式时,永远只会返回未访问状态的属性值,从根源上堵死通过样式差异探测浏览历史的可能。
由于font-weight不在:visited支持的属性列表中,你无法直接通过字重区分链接的访问状态,只能改用浏览器允许的属性做状态区分,参考写法:
body { font-size: 16pt; } a { /* 链接默认设置为粗体,作为未访问状态的基础样式 */ font-weight: bold; color: #1a73e8; text-decoration: underline; } a:visited { /* 用允许修改的颜色属性区分已访问状态 */ color: #5f6368; }
如果需要更明显的区分度,也可以搭配调整已访问链接的下划线颜色(属于border-color类属性,可生效)实现。
补充注意:编写链接伪类样式时建议遵循LVHA顺序(即:link → :visited → :hover → :active),避免同优先级下样式覆盖异常,不过你遇到的问题和书写顺序无关。
内容的提问来源于stack exchange,提问作者Richard

