超链接的lvha原则

一.lvha

实际上应该是lvfha,即:

a:link {/* 未访问过的超链接的样式 */}
a:visited {/* 访问过的超链接的样式 */}
a:focus {/* 拥有焦点的超链接的样式 */}
a:hover {/* 鼠标悬停的超链接的样式 */}
a:active {/* 被用户输入激活的超链接的样式 */}

这5个都是伪类,表示5种状态,其中linkvisited是超链接专用的,可以分类到链接伪类,而focushoveractive除了用于超链接还适用于其它元素,称为动态伪类

lvfha原则是说对超链接(带href属性的a标签)应用上面的5个伪类时,应该遵守这种固定的顺序

二.伪类与伪元素

伪类像类一样,用来选择DOM树上本就存在的某个元素。选择条件有两种:

  • 状态:元素是否处于某种特定状态,例如用户曾访问过(link/visited),此刻拥有焦点(focus),处于某种语言环境(lang

  • 结构:元素是否满足某种DOM结构方面的要求,例如身为长子的元素(first-child),以及CSS3新增的身为根元素的元素(root)和一大堆的结构化伪类(nth-**-of-type等等)

伪元素更像元素一些,用来选择DOM树上本不存在的元素(或某个元素的一部分)。比起伪类的繁荣大家族,伪元素就显得有些伶仃了,到目前(2017/11/4)为止,CSS3规范中仍然只有4个伪元素(CSS2.1就是4个):

  • 首字母:选择元素包含的文本内容的首字母(文本内容包含来自子元素的,也就是说可以跨标签层级选择文本)

  • 首行:选择元素包含的文本内容的首行(同上)

  • before:用于内容生成,在指定元素内容开头的位置生成一个元素(生成的内容位于元素内容区里)

  • after:用于内容生成,在指定元素内容结尾的位置生成一个元素(同上)

伪类与伪元素最大的区别是要选择的目标内容是否存在于DOM上,存在就是伪类,不存在就属于伪元素。换个角度看,想要为文档的某部分内容指定样式,那么先要(通过选择器)选中这部分内容,此时会遇到两种情况:

  • 目标内容恰好被某个标签包起来了,对这整个标签设置样式就能达到目的

  • 目标内容前后没有标签圈定范围,无法直接设置样式,需要插入一个临时标签把目标内容圈起来,再对这个临时标签设置样式

第一种情况通过伪类来处理,用伪类选择器把处于某种状态或具有某些结构特征的现有元素找出来,再应用样式。第二种情况要么手动插入额外标签,转化成第一种情况(有些场景通过添标签也做不到,比如首行,或者跨标签层级的场景),要么通过伪元素来解决,相当于请浏览器帮忙插入虚拟标签圈定目标内容,再应用样式

P.S.关于CSS3选择器的更多信息,请查看CSS选择器分类总结

三.a标签的6种状态

lvfha伪类给超链接提供了5种状态,第6种是指锚点,而不是超链接

link伪类存在的意义之一就是把超链接与锚点区分开,link伪类只匹配具有href的a标签(即超链接),而非锚点

一般桌面浏览器环境下,a标签的6种状态及对应的触发行为分别是:

a {/* 处于任意状态的a标签,不论是超链接还是锚点 */}
a:link {/* 未访问过的超链接 */}
a:visited {/* 访问过的超链接,点击超链接再返回当前页,这个超链接就处于visited状态 */}
a:focus {/* 获得焦点的超链接,tab键选中超链接或者长按超链接再移开鼠标 */}
a:hover {/* 鼠标悬停的超链接,鼠标经过超链接时或悬停在超链接上时,这个超链接就处于hover状态 */}
a:active {/* 处于激活状态的超链接,鼠标在超链接上按下时 */}

其中focus, hover, active不太好区分,focus是一种延续性状态,而hover, active短暂性状态,进一步细分hover, active的话,后者是前者的一种特殊状态(触摸设备除外),例如:

a:focus {border: 1px solid green;}
a:hover {border-color: red;}
a:active {border-style: dashed;}

那么下列连续操作对应的状态和样式分别是:

按下tab键 -> focus -> 绿色实线边框
点击其它空白处 -> a & link | visited -> 对应样式
鼠标划过时 -> hover -> 无边框
鼠标悬停时 -> hover -> 无边框
鼠标按下 -> focus & hover & active -> 红色虚线边框
鼠标移到超链接之外再抬起 -> focus -> 绿色实线边框
(不点击其它地方的话,超链接将一直处于focus状态)
鼠标划过时 -> focus & hover -> 红色实线边框

正因为focus是一种延续性状态,所以要放在短暂性的hover, active之前,否则最后鼠标划过时不会表现出hover样式(根据层叠规则,先声明的hover会被focus覆盖掉)

因为focus, hover, active3个状态有重叠,所以建议保持特定的声明顺序,让层叠结果符合样式表编写者的预期。而linkvisited是互斥的,不存在重叠,所以二者的相对顺序并不重要(vlfha也是合理的,“爱恨”顺序只是好记)。同样,由于link/visited是永久性状态,为了让短暂性状态和持续性状态有表现机会,就把focus/hover/active放在后面,让长状态的层叠优先级更低一些,所以就有了lvfha原则

另外,规范没有明确说明focus, hover, active对应的状态的起止条件:

CSS没有定义哪些元素可以处于上面的状态,以及这些状态怎样进入和离开。脚本可以改变元素是否对用户事件做出响应,并且不同的设备和UA指向和激活元素的方式不同

CSS 2.1没有定义如果一个’:active’或者’:hover’元素的父级是不是也处于这种状态

(摘自5.11.3 动态伪类: :hover,:active与:focus)

所以不能确定动态伪类的触发行为,也无法确定这几个伪类适用于哪些元素(表单元素、div等可能支持也可能不支持),都取决于用户代理的实现

四.组合伪类

建议遵循lvfha顺序是考虑层叠规则,否则可能会被覆盖,导致同名规则无效。例如:

a:hover {text-decoration: underline overline;}
a:link {text-decoration: none;}
a:visited {text-decoration: none;}

hover样式(小技巧:鼠标划过时同时显示上划线和下划线)永远不会生效,因为text-decoration属性总会被下面两条之一覆盖掉

当然,前提条件是样式规则存在冲突(同名属性且来源、重要性、特殊性都相同)时,根据声明顺序来解决冲突,此时lvfha顺序才真正起作用。换句话说,如果不存在样式冲突,声明顺序并不重要

也就是说,通过其他方式避免样式冲突发生,就不用遵守lvfha顺序了,例如通过组合伪类来把状态展开

/* 不要求顺序 */
:link
:visited
:link:hover
:visited:hover
/* 要求顺序 位于上2行之后 */
:link:active
:visited:active
/* 或者替掉上2行 不要求顺序 */
:link:hover:active
:visited:hover:active

展开之后就没有重叠状态了,让每条规则都变成严格互斥的,自然就没冲突了

P.S.注意:因为IE6-不能正确处理组合伪类,只认最后一个,所以lvha应用更广(实际上组合伪类的语义更明确,没有“隐藏的奇怪规则”)

另外,可以层叠规则来实现特殊效果,例如:

// 用lhva实现只有未访问的链接才有hover效果
a:link {}
a:hover {}
a:visited {}
a:active {}

很有意思的小技巧,相当于:

a:link:hover {}

这就体现了组合伪类语义明确的优势

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code