Lajos Arpad Lajos Arpad - 4 months ago 19
jQuery Question

Will future versions of CSS support parent or ancestor selector?

Let us suppose that I have a theme where I am designing among others

form
s. Let us suppose further that the
form
might have a
submit
button of class
.submit
. There are cases when the button is
disabled
(the
form
is not ready for submission) and when the button is not
disabled
. I can design the button like this:

.submit {
/*Some Rules*/
}

.submit:disabled {
/*Some Rules*/
}


However, if I know that the button will be inside a
div
and I want to style its parent based on the
disabled
property, then I need to implement a
disable
and an enable
function
and to style
parentElement
in those
function
s. Instead of that I would like to be able to define rules like this:

.submit < div { /*where < would mean parent*/
/*Some rules*/
}

.submit:disabled < div {
/*Some rules*/
}


Currently this is not supported.

Let us suppose further that I want to style the
form
based on the button's
disabled
attribute, to visually show whether the
form
is ready for submission. In this case I need to find the ancestor
form
tag (if exists) and style it whenever
disable
or
enable
is called. Instead of that, I would like to be able to do something like this:

.submit << form { /*Where << means ancestor*/
/*Some rules*/
}

.submit:disabled << form {
/*Some rules*/
}


Instead of this simple and straightforward way, currently we need to do something like this:

function disable(element) {
element.disabled = true; //Disabling the button
var p = element.parentElement; //Here we need to style p with Javascript
//Find the form element
var formExists = true;
while (formExists && (p.tagName !== "form")) {
if (p.tagName === "html") {
formExists = false;
} else {
p = p.parentElement;
}
}
if (formExists) {
/*Do something with p*/
}
}

function enable(element) {
element.disabled = false; //Disabling the button
var p = element.parentElement; //Here we need to style p with Javascript
//Find the form element
var formExists = true;
while (formExists && (p.tagName !== "form")) {
if (p.tagName === "html") {
formExists = false;
} else {
p = p.parentElement;
}
}
if (formExists) {
/*Do something with p*/
}
}


Despite appearance,
disable
and
enable
is not code-duplication, since we intend to add different styles. Off course, this code can be refactored somewhat, but it is ugly, difficult to maintain, hacky, not to mention the fact that it assumes that one calls
disable
or
enable
whenever the state of the
disabled
attribute needs to be changed. While all the problems shown above is solvable even without parent/ancestor selector, I would prefer to be able to implement the theme inside a .css file, without Javascript hacks, without assuming that whenever a button gets
disabled
disable
is called and whenever a button gets enabled
enable
is called. Also, if this kind of thing could be triggered by CSS, then we could use automatically browser-level juices which could benefit performance.

An alternative is to have a
class
defined for a
form
, but then that
class
needs to be maintained programmatically along with the
disabled
attribute and things are not much better.

I believe I have proved in my question that such CSS rules would be superior compared to Javascript ui hacks from several points of view:


  • parents and ancestors could be designed inside a single .css file

  • developers would not have to worry about calling helper functions, nor about telling new team members those policies or documenting the usage of the theme

  • there would be no need to write difficult-to-maintain Javascript functions to handle ui changes

  • we could override the rules as we like with other CSS rules if needed instead of escalating the hacks to even bigger hacks



I know parent and ancestor selectors are not among the CSS features we can use, so I wonder whether the situation will improve. Are there known plans for this kind of feature? Are these scheduled for a future date?

Answer

Yes. In CSSv4 will be the :has() pseudoselector. The problem with parent elements is old, and your suggestion is wide known:

 element < parent {} 

Will have a serious problem. Browser reads the CSS from right to left, so this kind of selectors will be low performance.

Related link (suggestion in year 2008): http://shauninman.com/archive/2008/05/05/css_qualified_selectors

To achieve that, in CSS4 we can do:

 element:has(.parent) {}

And this will be perfect! It can select an element that is contained by the .parent selector. We can do more things like:

 element:not(:has(.parent)) {}
 element:has(#myId > .myClass) {}

This is currently not supported by any browser. It's a working draft and will came with CSS level 4.

Related links:

While this awesome selectors are not available to general use, you must to do it with Javascript or thinking about another HTML structure to avoid the needed of parent selector.

EDITION:

I've found right now a polyfill that allows you to target parent elements with CSS. It's written in jQuery but it translates the CSS content,so you don't need to make anything to make it works (apart of plugin inclusion)

https://github.com/Idered/cssParentSelector

Quick Info extracted from the plugin page:

! - determines subject of selector according to CSS4 reference

E > F - selects an F element, child of E

E! > F - selects an E element, parent of F

CSS4 reference

In the past, this syntax was used to develop the parent element selector, but due performance reasons it was discarded. Here you are one link explaining it:

http://red-team-design.com/css-parent-selector/

ANOTHER TECHNIQUE

It's a technique that allows you to think different in pure CSS to achieve the same functionality (stylize parent elements). You can see in this link:

https://escss.blogspot.com/2014/02/parent-selector-pure-css.html

It explains how to select parent elements without weird things, plugins, or polyfills. It's only pure CSS but with a expensive thinking behind.