Jeroen
Jeroen

Reputation: 16815

How to have an arbitrary selector in CSS?

How do I add arbitrary selectors in CSS rules?

For instance, say I want to make every item of the .effect class turn red if the user hovers over#target. How would I implement this? Is there a robust approach to this? I know about things like nesting .effect inside #target or using the sibling selectors ~ and +, but all of these rely on a certain way of structuring the HTML.

Is this possible at all? It seems like something relatively straight forward. If it's not possible, is there any reason it isn't?

I do not want to use Javascript.

Upvotes: 1

Views: 1032

Answers (1)

Luke Briggs
Luke Briggs

Reputation: 3789

No, you can't.

Don't expect it for the forseeable future either. Let's take a look why!

From the point of view of someone who works on a CSS engine, selectors are actually evaluated backwards. This is certainly a rather interesting and less known aspect of CSS; Whilst the CSS selector specification does not directly define the implementation behaviour, all selectors are defined with this in mind. No hierarchial/ 'structural' selector has been created which can arbitrarily jump around the DOM as that would cause major performance issues in comparison to what we have today.

So, for example, let's take the following selector:

#target:hover .effect

This requires that an element with a class of effect is a child (at any depth) of an element with an ID of target because the selector engine starts by matching elements with a class of effect first, then proceeds to work backwards, stepping up the DOM looking for a parent element with an ID of target next.

Jumping to the parent node is extremely fast. Evaluating this in the forward direction would involve testing all children of any element with an ID of target which is considerably more performance intensive.

This characteristic of CSS evaluation is naturally important for performance; at the worst case, the above selector will only bubble up to the root of the DOM, testing only a handful of elements along the way. The direct child selector, a > b, only tests the direct parent and then stops, for example.

'Baking' the structure of a selector

For even further performance, the structure of a selector is 'baked' into the DOM. There certainly isn't consensus on this, i.e. every CSS engine does it differently, but roughly when the DOM structure of a selector matches (i.e. we have found an element with a class of effect and any parent with an id of target) the selector is recorded as having matched in the DOM, regardless of the hover state on #target. When the hover state on #target changes, it then simply bumps all the selectors that are baked at the element - this may then trigger the whole selector to activate or deactivate. This way we're not constantly testing masses of elements when the mouse moves around, for example.

In short, none of this works either if it could arbritarily jump around the DOM. Elements entering/ leaving the DOM could affect selectors in entirely separate parts of the DOM, so the style engine would potentially be checking the entire DOM to keep this index up to date.

Partially loaded DOM

Also consider that we can test for elements before something, but not after (when evaluated backwards):

h1 + h2
h1 - h2 /* ..? Doesn't exist! */

This is because when we test this particular selector starting against a 'h2' element, the DOM following it might not actually be loaded yet. As soon as an element enters the DOM, either because it's just been parsed from the incoming HTML or it has been added via scripting, we begin checking for which selectors it matches. That means we can't depend on anything being available after the element in the raw HTML, so again this would also be a block for any arbritary DOM hopping.

In Summary

It's currently not possible and it's unlikely to be added any time soon because it invalidates multiple performance characteristics of CSS implementations. That's not to say that it won't be added however; the W3C does appreciate that hardware is getting ever more powerful, so there is always a point at which author convenience will win over implementation performance considerations.

So, putting this a little further into context of the question, take a look at this fiddle created by @Marvin to see what's currently possible with selectors today.

Upvotes: 3

Related Questions