Skip to main content

The LVHA Principle of Hyperlinks

Free2017-11-04#CSS#link-visited-hover-active#link-visited-focus-hover-active#CSS lvha#CSS lvfha#CSS爱恨原则

Why does link have the LVHA principle? What is the fundamental reason?

1. LVHA

Actually, it should be LVFHA, namely:

a:link {/* Style for unvisited hyperlinks */}
a:visited {/* Style for visited hyperlinks */}
a:focus {/* Style for hyperlinks with focus */}
a:hover {/* Style for hyperlinks on mouse hover */}
a:active {/* Style for hyperlinks activated by user input */}

These 5 are all pseudo-classes, representing 5 states. Among them, link and visited are specific to hyperlinks and can be classified as link pseudo-classes, while focus, hover, and active apply to other elements besides hyperlinks and are called dynamic pseudo-classes.

The LVFHA principle states that when applying the above 5 pseudo-classes to hyperlinks (a tags with href attribute), this fixed order should be followed.

2. Pseudo-classes and Pseudo-elements

Pseudo-classes, like classes, are used to select an existing element on the DOM tree. There are two types of selection conditions:

  • State: Whether an element is in a certain specific state, such as whether the user has visited it (link/visited), currently has focus (focus), or is in a certain language environment (lang)

  • Structure: Whether an element meets certain DOM structural requirements, such as being the first child element (first-child), and the root element (root) newly added in CSS3, and a bunch of structural pseudo-classes (nth-*, *-of-type, etc.)

Pseudo-elements are more like elements, used to select elements that do not exist on the DOM tree (or a part of an element). Compared to the prosperous family of pseudo-classes, pseudo-elements appear somewhat lonely. As of now (2017/11/4), there are still only 4 pseudo-elements in the CSS3 specification (same as CSS2.1):

  • First letter: Selects the first letter of the text content contained in an element (text content includes that from child elements, meaning text can be selected across tag hierarchy levels)

  • First line: Selects the first line of the text content contained in an element (same as above)

  • Before: Used for content generation, generates an element at the beginning of the specified element's content (the generated content is located in the element's content area)

  • After: Used for content generation, generates an element at the end of the specified element's content (same as above)

The biggest difference between pseudo-classes and pseudo-elements is whether the target content to be selected exists on the DOM. If it exists, it's a pseudo-class; if not, it belongs to pseudo-elements. From another perspective, if you want to specify styles for a certain part of a document, you first need to select that part (through selectors). At this point, you'll encounter two situations:

  • The target content happens to be wrapped by a tag, and setting styles for the entire tag achieves the purpose

  • There are no tags before and after the target content to define the scope, so styles cannot be set directly. A temporary tag needs to be inserted to wrap the target content, and then styles are set for this temporary tag

The first situation is handled through pseudo-classes, using pseudo-class selectors to find existing elements in certain states or with certain structural characteristics, then applying styles. The second situation either requires manually inserting extra tags to convert it to the first situation (some scenarios cannot be achieved even by adding tags, such as first line, or scenarios across tag hierarchy levels), or solving it through pseudo-elements, which is equivalent to asking the browser to help insert virtual tags to define the target content, then applying styles.

P.S. For more information about CSS3 selectors, please see CSS Selector Classification Summary

3. Six States of the a Tag

LVFHA pseudo-classes provide 5 states for hyperlinks. The 6th state refers to anchors, not hyperlinks.

One of the meanings of the existence of the link pseudo-class is to distinguish hyperlinks from anchors. The link pseudo-class only matches a tags with href (i.e., hyperlinks), not anchors.

In a general desktop browser environment, the 6 states of the a tag and their corresponding trigger behaviors are:

a {/* a tags in any state, whether hyperlink or anchor */}
a:link {/* Unvisited hyperlink */}
a:visited {/* Visited hyperlink. Click the hyperlink and return to the current page, and this hyperlink is in the visited state */}
a:focus {/* Hyperlink that has gained focus. Select the hyperlink with Tab key or long-press the hyperlink then move the mouse away */}
a:hover {/* Hyperlink with mouse hovering. When the mouse passes over or hovers on the hyperlink, this hyperlink is in the hover state */}
a:active {/* Hyperlink in activated state. When the mouse is pressed down on the hyperlink */}

Among them, focus, hover, active are not easy to distinguish. focus is a continuous state, while hover, active are brief states. Further subdividing hover, active, the latter is a special state of the former (except for touch devices). For example:

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

Then the states and styles corresponding to the following continuous operations are:

Press Tab key -> focus -> green solid border
Click other blank area -> a & link | visited -> corresponding styles
When mouse passes over -> hover -> no border
When mouse hovers -> hover -> no border
When mouse is pressed -> focus & hover & active -> red dashed border
Move mouse outside the hyperlink then release -> focus -> green solid border
(If you don't click elsewhere, the hyperlink will remain in the focus state)
When mouse passes over -> focus & hover -> red solid border

Precisely because focus is a continuous state, it should be placed before the brief hover, active. Otherwise, when the mouse passes over at the end, it won't show the hover style (according to cascade rules, the previously declared hover will be overridden by focus).

Because the 3 states focus, hover, active overlap, it's recommended to maintain a specific declaration order so that the cascade result matches the stylesheet author's expectations. Meanwhile, link and visited are mutually exclusive and don't have overlap, so their relative order is not important (VLFAH is also reasonable; the "LoVe HAte" order is just easy to remember). Similarly, because link/visited are permanent states, to give brief states and continuous states a chance to manifest, focus/hover/active are placed afterward, making the cascade priority of long states lower, hence the LVFHA principle.

Additionally, the specification doesn't clearly specify the start and end conditions of the states corresponding to focus, hover, active:

CSS does not define which elements can be in the above states, and how these states are entered and exited. Scripts can change whether elements respond to user events, and different devices and UAs point to and activate elements in different ways.

CSS 2.1 does not define whether the parent of an ':active' or ':hover' element is also in that state.

(Excerpted from 5.11.3 Dynamic pseudo-classes: :hover, :active, and :focus)

Therefore, the trigger behavior of dynamic pseudo-classes cannot be determined, nor can it be determined which elements these pseudo-classes apply to (form elements, div, etc. may or may not support them); it all depends on the user agent's implementation.

4. Combined Pseudo-classes

It's recommended to follow the LVFHA order considering cascade rules; otherwise, it might be overridden, causing same-name rules to become invalid. For example:

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

The hover style (small trick: display both overline and underline when the mouse passes over) will never take effect because the text-decoration property will always be overridden by one of the two rules below.

Of course, the prerequisite is that when style rules conflict (same-name properties and source, importance, specificity are all the same), conflicts are resolved according to declaration order. Only then does the LVFHA order truly take effect. In other words, if there's no style conflict, declaration order doesn't matter.

That is to say, by avoiding style conflicts through other means, there's no need to follow the LVFHA order. For example, by combining pseudo-classes to expand the states:

/* Order not required */
:link
:visited
:link:hover
:visited:hover
/* Order required, must be after the above 2 lines */
:link:active
:visited:active
/* Or replace the above 2 lines, order not required */
:link:hover:active
:visited:hover:active

After expansion, there are no overlapping states, making each rule strictly mutually exclusive, so naturally there's no conflict.

P.S. Note: Because IE6- cannot correctly handle combined pseudo-classes and only recognizes the last one, LVHA is more widely applied (actually, the semantics of combined pseudo-classes are clearer, without "hidden strange rules").

Additionally, cascade rules can be used to achieve special effects. For example:

// Use LHVA to implement hover effect only for unvisited links
a:link {}
a:hover {}
a:visited {}
a:active {}

A very interesting little trick, equivalent to:

a:link:hover {}

This demonstrates the advantage of combined pseudo-classes having clear semantics.

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment