kizu kizu - 1 month ago 5
CSS Question

Why is hover for input triggered on corresponding label in CSS?

Am I missing something, or the behavior of this example — http://dabblet.com/result/gist/1716833 — is rather strange in Webkits/Fx?

There is an input with label and the following selector:

input:hover + .target {
background: red;
}


And this style is triggered when you hover the
label
attached to the
input
, not only the
input
itself. Even more: there is a difference between the
label
with
for
and
input
wrapped in a
label
— if you'd hover the
input
at first and then move the cursor straight to the
.target
— the strange hover won't trigger in wrapped version.

And this is only reproduces in Firefox and Safari/Chrome, but in Opera it's ok.

So, the questions is: if this issue is described somewhere in specs? I couldn't find any appropriate place that describes it and tells what behavior is right.

Answer Source

This is in the HTML spec now; it wasn't until the October 2012 WD that it was added to W3C HTML5 (emphasis mine):

The :hover pseudo-class is defined to match an element "while the user designates an element with a pointing device". For the purposes of defining the :hover pseudo-class only, an HTML user agent must consider an element as being one that the user designates if it is:

  • An element that the user indicates using a pointing device.

  • An element that has a descendant that the user indicates using a pointing device.

  • An element that is the labeled control of a label element that is currently matching :hover.

Identical text appears in the living spec.


I discovered this very behavior a few years ago on the previous design of my site's contact form, where label:hover also triggers :hover on any form input element that is either its descendant or referenced by its for attribute.

This behavior was actually added to a recent build of Gecko (Firefox's layout engine) in this bug report along with this (rather short) mailing list thread, and it was implemented in WebKit many years back. As you note, the behavior doesn't reproduce in Opera; it looks like Opera Software and Microsoft didn't get the memo.

All I can find in the spec that could relate to this behavior somehow is here, but I don't know for sure (italicized note by me):

  • The :hover pseudo-class applies while the user designates an element with a pointing device, but does not necessarily activate it. For example, a visual user agent could apply this pseudo-class when the cursor (mouse pointer) hovers over a box generated by the element.

[...]

Selectors doesn't define if the parent of an element that is ‘:active’ or ‘:hover’ is also in that state. [It does not appear to define the same for the child of an element either.]

Note: If the ‘:hover’ state applies to an element because its child is designated by a pointing device, then it's possible for ‘:hover’ to apply to an element that is not underneath the pointing device.

But what I can conclude is that this behavior is by design in at least Gecko and WebKit.


Regarding what you state here:

Even more: there is a difference between the label with for and input wrapped in a label — if you'd hover the input at first and then move the cursor straight to the .target — the strange hover won't trigger in wrapped version.

Given the above behavior, the only possibility left here is that you've simply been bitten by the cascade.

Basically, this rule:

/* 1 type, 1 pseudo-class, 1 class -> specificity = (0, 2, 1) */
input:hover + .target {
    background: red;
}

Is more specific than this rule:

/* 1 class, 1 pseudo-class         -> specificity = (0, 2, 0) */
.target:hover {
    background: lime;
}

So in applicable browsers, the label.target by your first checkbox will always be red on hover, because the more specific rule always takes precedence. The second checkbox is followed by a span.target, so none of this behavior applies; only the second rule can take effect while the cursor is over the span.target.