Search code examples
javascriptjqueryhtmlcssparent-selector

Will future versions of CSS support parent or ancestor selector?


Let us suppose that I have a theme where I am designing among others forms. 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 functions. 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?


Solution

  • 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):

    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)

    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:

    Alternative 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:

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