CSS selector for :parent targeting (please)

Me this weekend:

I can’t count the number of times I’ve cursed CSS for not having a :parent pseudo selector: a img:parent { background: none; }

The what followed was some going back and forth with people who have thought this properly through.

:has selector

Originally I felt that a :parent pseudo selector would do the trick, but Stuart pointed out that selectors syntax doesn’t work that way, it’s always left to right determines depth.

So thinking about it some more, I felt that using a :has() selector would complement the :not() selector really well. Something like this:

a:has(img) { background: none; }

Except that’s completely wrong.

The E:not(s) selector applies to the particular element to which it’s applied. Where as E:has(s) applies what element E contains – therefore it’s asking a ton more for the browser to do.

Jonathan Snook helped shed a lot of light on this pretty core performance problem, and this weekend followed up with a detailed post about the potential performance issues with a :has() selector. But I don’t think the book is quite closed on this one, but I do agree that a :has() selector is going to be almost so expensive on the render engine that it wouldn’t make sense to implement.

I should also add that Shaun Inman’s E < F proposal is the same as E:has(s) – and therefore I don’t think it has legs.

So maybe back to the :parent selector.

:parent selector

Jonathan has a really useful example of how the :last-child selector works and how it applies live in the browser (see the section called “How do browsers actually handle this”). What we see is the browsers are looking for the closing element. A child element, regardless of it’s position, always has a parentNode (I’m talking DOM right now).

Again, Jonathan points out that the browser treats the markup that comes down the wire as a stream (this is particular clear in the render pause demo), and the CSS selectors are applied to that stream as the elements are built in the DOM.

The problem with E:has(s) is once element E is found in the DOM, every new element rendered in to the DOM would require a run evaluation of the :has selector. Bottom line: bad idea.

The difference between E:has(s) and E:parent is that :parent is reference the parentNode which is one particular element. Once the element E is found in the DOM it would then just refer to it’s parent and apply the style.

It would look like this:

a img:parent { background: none; }

Performance-wise, I can’t see how this is any different to a regular selector, except that once it’s matched, it needs the parentNode – again, something that’s available immediately as the element E is rendered.

Syntax-wise, yes, it’s a departure from every other syntax style, but that’s not a good enough reason not to include it. There were several hear hear‘s to my groan on Sunday, and it would be an invaluable addition to native CSS selectors.

How do we get this in browsers?

Actually, I’ve no idea. Maybe you’re reading and you are or know someone on the CSS working group. Maybe you know a friend of a friend. Go shake them and point them to this article.

Maybe this is a really bad idea for reasons that I’ve not spotted: tell me, explain in the comments.

Do we need a :parent selector? I think we do, and I’ve wanted it for a very long time now.

36 Responses to “CSS selector for :parent targeting (please)”

  1. I told you this exact thing! http://twitter.com/rudiedirkx/status/26941629852 Within a semi-selector (eg “img:parent” in “table a img:parent”) the order is left to right, so it shouldn’t be nearly as intensive as “table a:has(img)” would be.
    Unfortunately, I don’t know the browserbigshots.

  2. Please, please, please let this happen.

  3. What do you think about the “CSS Qualified Selectors” proposal by Shaun Inman?

    It seems more generic to me, and very handy!

    Taking your example:
    a< img { background: none; }

  4. Obviously this has been discussed (at length) before (I’m definitely not the first to suggest this!).

    Dave Hyatt of Webkit fame commented back in 2008 explaining that an E < F selector would be a nightmare in performance - but E < F is the same as E:has(s) which I'm not proposing here.

    One of the main hazards of E:has(s) is that you could be targeting the entire document if the selector isn't written properly. Whereas E:parent only targets the `parentNode` which (I would think - but could easily be wrong) has the same optimisation benefits of F > E – because it’s a direct descendant and you don’t have to go searching through the DOM – only the child elements.

  5. @Pierre – sorry, our comments got posted across each other. I’ll update the blog post with this, but: Shaun’s proposal is exactly the same as E:has(s) – which has the exact same performance problems that Jonathan pointed out.

    I’m pretty much on Dave Hyatt’s side when he explains the optimisation issues that would be faced over something that we’ve lived for so long without.

    My feeling is that E:parent is a lot less expensive with regards to performance – but I need someone with real experience to either validate or deny that.

  6. What happens with descendant selector constructs like:

    div blockquote:parent ol {
      position: absolute;
      top: 0;
      left: 0;
    }

    In the case of a <div> that contained an <ol> followed by many <p> elements followed by a <blockquote>, all of the content up to the occurrence of the <blockquote> that triggered the application of that rule would have to be reflowed. I know this seems a little contrived, but it demonstrates that the potential for very (computationally) expensive consequences is there.

    An HTML5 example shows that we don’t even need descendant selectors to cause chaos with inherited properties:

    footer:parent {
      background-color: #f0f;
      color: #0f0;
      font-size: 6em;
    }

    would mean almost the entire page had to be re-rendered, assuming the <footer> element was near the end of the page and a child of <body>.

  7. As far as I can tell it has the same problem. It affects the styling of an element for which styling is already calculated. And it may already be shown given incremental display and slow loading of the markup.

    Further, given A>B:C C is a property of B and cannot start referring to A. We do not want to change those principles of Selectors.

  8. As Stuart mentioned in the tweet queue, it seems awful that developers have to rely on their favourite javascript library in order to make something which seems like a much better way to manage the cascade?
    Also, I have to repeat my thanks to you, Stuart and Jonathan for a better understanding of CSS decendant selectors because of this!
    Ta v. much!
    J

  9. [...] CSS Selector for :parent Targeting (Please) [...]

  10. I’d love to see some ability to reference a parent element in CSS too. I’m in the same boat with you, I’ve often found wishing I had the ability to reference a parent via CSS, but end up working around it w/either server-side or client-side technology.

  11. @nick – now we’re talking – these are the types of questions that should be asked.

    So my two penny opinion, the second footer:parent: every element that comes in to the DOM causes a reflow.

    In fact, you’ve got the same problem here:

    :last-child {
      font-size: 6em;
    }

    Whilst the browser is build the DOM, the :last-child matches every element as each element is added to the tree, and removing it from the previous last child each time – thus causing the constant reflow.

    However, Jonathan’s examples show how some browsers protect against that, and in fact keep the :last-child selector back for only once the parent element has been closed. Arguably if that’s what’s done with :last-child, surely the same could be applied to :parent?

    If this were the technique applied, then it would also explain how to optimise div blockquote:parent ol, in that it would be two selectors, run last (once the parent element is closed), as per :last-child.

  12. Yes, using a :parent selector to evaluate at the time of the child element will be better than the :has syntax. There’s are other issues that you’d need to work out, like the one Nick mentions. Which also has a performance impact—although, not as bad as :parent. The computed style for the parent element has been calculated but now need to be re-calculated and re-rendered for the parent and all child elements. If you have multiple children (that match the same rule) in which the :parent selector applies, does it need to re-calculate every single time? (My instinct says yes but suspect browsers could optimize for this scenario.)

    There’s also specificity issues to think about.

    For example,

    HTML: <div><img> text </div>
    CSS:
    div:first-child { color:blue; }
    img:parent { color:green; }

    What colour should the text be?

    Like with :has, it’s not impossible. It’s just a matter of whether we are willing to deal with the trade-offs to have it. :)

  13. The only issue I can think of with the :parent idea is you can effectively walk backward up the DOM which could lead to more selector evaluation (but, I guess browsers could normalize the selector chain)

    <body>
      <div>
        <h1><a href="#">Linky</a></h1>
      </div>
    </body>

    You could effectively style the body using:

    a:parent h1:parent div:parent { background: blue }

    Or even worse…

    a:parent *:parent *:parent { background: blue }

    But, if it turns out this is acceptable, and the theory is to only access the immediate parent (via parentNode) then :nth-parent(x) could be useful addition for looking further back (provided only integers were allowed as an argument)

    These issues always boil down to how the browsers native performance is affected and I’m really not sure how we, the developer, can measure the impact this would really have on page performance.

  14. I agree that a :parent selector would eliminate a lot of hacky javascript solutions to styling elements the way we need it them look. I’m no browser performance expert, but it seems to me that there are a lot of things already in place that can be written poorly to generate really crappy code and bring a browser to it’s knees. I don’t think that the fact that a selector can be abused and create a performance problem is enough to keep it out, when using it properly would benefit everyone.

    Think about the performance we lose in going over the DOM with javascript to style those elements after they have already been rendered. And the FOUC that happens with that approach.

    That’s kind of like saying the * selector should be eliminated because I could preface everything in my CSS with * and that would slow down the rendering. Just write better code!

    Rem, thanks for starting up another discussion – here’s to hoping it goes somewhere.

  15. MooTools new Slick.js standalone selector engine supports the direct parent combinator and many others. They are also all namespaced to allow for the w3c to possibly standardize them.

    http://github.com/mootools/slick

    MooTools 1.3 will be released soon and will be using tons of features in Slick.js.

  16. It’s a very bad idea for many reasons but the most immediate one is probably the syntax… Honestly, it’s unreadable. Pseudo-classes characterize the element that is the “subject” of the current sequence of simple selectors. Your “:parent” cannot mean “defer to the parent”. I read it as “this img element is a parent”…

    A property like (thinking out loud) defer-styles: parent is more conceptually in line with what you want but still poses big issues like the one Anne van Kesteren mentioned above.

    :has() and :matches() and selector’s subject indicator are old stories in the CSS WG. Not sure we’ll see them appear any time soon, that’s really not an easy subject.

    Daniel Glazman, W3C CSS WG Co-chair

  17. Would be nicer to specify which part of the selector would match if whole selector applies – how do I modify the target if I also want to include other pseudo-classes or :hover, :focus etc?
    Not mentioning the limit :parent applies, as you can not select further up the tree.

    To pinch from @sil http://identi.ca/conversation/55019160#notice-55488414

    but change it to something like a :this pointer,
    a span:this img:hover { display:block; etc.. }

    Feel free to rename.

  18. Scratch what I said above – it’s cobblers. You couldn’t walk back up the tree that way since each node is a descendant. Fool!

  19. If you want to see the difference in performance associated with various forms of parent selector, then why not take a crack at implementing them in a JS selector engine like sizzle and experiment?

    But, i’m failing to see what it would bring to the table that a simple class would not solve more efficiently?

  20. Agreeing with Daniel Glazman on the syntax. Given the existing nomenclature pattern of CSS pseudo-classes, “:parent” just doesn’t mean what’s being discussed here, especially to those of us who thrive on consistent patterns for usability.

    OTOH, perhaps taking the concept of “:has()” and splitting it into “:has-child()” and “:has-descendant()”. Or maybe “:parent-of()” and “:ancestor-of()” would read better?

    Either way, both of these concepts still have performance concerns, but (as some have already pointed out) one’s might compare more to those of the existing “:last-child” than the potential browser-spasms that might result from the other.

    Ponderponderponder…

  21. Daniel’s right; Pseudoclasses are, well, pseudoclasses – pretend classes that get added to an element when building the CSS element-tree. “a img:parent” does not and can not mean “select the A if it’s the parent of an IMG”, because the A is not at the end, and the :parent is modifying the IMG.

    :has() is the way to go. “a:has(img)” expresses exactly what you want, and has the advantage of actually being somewhat more powerful than a straight “parent selector”, because it can be used in a drive-by fashion to qualify stuff that isn’t at the very end of a selector.

    Yes, there are astonishing efficiency concerns here. I can’t overstate how bad a descendant selector in :has() can be in very common cases. We can work around this, though. Rather than the general :has(), produce a :has-child() pseudo. Testing for the existence of children produces a significant perf hit, but not nearly so bad as testing for general descendants. Similarly, a :has-sibling() can test for following siblings, which is only slightly worse for performance than child-testing.

    Now, a general note to everyone who tries to compare this to javascript-based selector engines, or who proposes testing it in js-based selector engines: it will do you no good. The environment that the browser’s CSS selector-matcher runs in is *completely* different from the environment that js-based selector engines run in; namely, the js-based ones get to run on a complete DOM, where testing an element’s children is easy because they already exist. In the browser, 99% of all selector matching happens during page load, where you want to match as fast as possible *while the page is being built*. In ordinary selectors, this is fine – the standard suite of selectors only moves down children and forward siblings. If you read the selector in reverse, then, you start with the element that will be matched, and then just need to walk up the tree, testing ancestors and previous siblings, to see if the match is valid. If the node in question *just* got added to the tree, its ancestors and previous siblings already exist (they were created by markup earlier in the stream), so you can do that easily, but children and following siblings don’t exist yet.

    That’s why :has() is such a perf hit – you can no longer match on the information available to you at the moment an element gets added to the DOM – you have to wait until later and then revisit the element. This also means that any inherited values that were already calculated may change, which can introduce layout changes.

    That said, :has-sibling() is essentially equivalent to :last-child, and better than :nth-last-child(). So, the perf hit of :has-sibling() and :has-child() should be generally acceptable; we just need to get browsers to implement them.

  22. It may be easier to FIND the right element using :parent, but what about inheritance? You’d expect that all the childs of the parent will inherit it’s values, just as with any other selector. This would require the browser to re-paint every child element again after a :parent pseudoselector.

    Specificity may solve this, to a degree, but it’d still be quite a performance sucker.

    I’ve also very rarely found myself in the need of a :parent selector, and each and everytime there’s been a solution that was simple, clean and elegant.

  23. > Performance-wise, I can’t see how this is any different to a regular selector

    Browsers don’t look for elements that match selector, they look for selectors that match the element.

    ‘a’ is easy. You take tag name and compare.

    ‘p > a’ is easy too. You check if this element’s tag name is ‘a’, and then check if element.parentNode.tagName is ‘p’.

    ‘a:parent’ is completely different. You must assume that current element matches if childNodes > 0, and then _scan all child elements_ to find if there’s ‘a’ among them.

  24. To be honest, we’re all looking at a panacaea here: an HTML document that we can style without having to use any IDs or classes and a CSS style sheet that we could apply to practically any HTML document and get a perfectly workable webpage. To be honest, I don’t know if that’s possible.

    As someone who’s worked in the Web field for almost a decade now, I’ve noticed hundreds of times when a parent selector would’ve saved me logic and code. To be realistic, though, a parent selector would double (at least!) the amount of DOM traversing, reverse the natural parse order of the language, and introduce so much risk for rendering incongruities across browsers (off the top of my head: something like article:parent ~ aside:parent > p:parent { }, where you’ve got a whole bunch of words and none of them are actually the ones being styled!).

    At the end of the day, it’s one selector, whose functionality 95% of the time can be achieved by giving the parent element an ID or assigning the style to the element using a child or descendant selector instead, both of which involve no backwards DOM traversal. I’m all for CSS evolving and new selectors being introduced, but a parent selector is just not practical.

  25. there has definitely been times when i would’ve like a selector like :parent, but i can see the perf hist as described in other posts – i suppose i get sick of id/class markup when i feel a selector should do the trick….

  26. I’ve certainly encountered a need for this, but usually only in the context of not being able to adjust the markup of something- usually in a customer’s marketing CMS or something like that, where markup that is predefined and not editable is being utilized- but really needing to make some style adjustments on an element not particularly well-classed or ID-ed (if a class name or ID has been supplied at all to the parent element in question).

    As such, and given the theoretical performance hit (which is backed up with some pretty sound concepts against its implementation), I think it’s probably best left alone, else we might start seeing some pretty gnarly and poorly-constructed styles.

    As much as I may have pined for it in certain instances, I definitely feel like :parent (or any analog) is a CSS Pandora’s Box (and should not be opened).

  27. Assuming you are using links with images only, and no text, you could nix your a rules with a a:empty{...} selector. That would at least take care of half of the issue.

    a:empty{
    text-decoration:none;
    border:none;
    }

  28. I’ve made something like this http://demo.idered.pl/jQuery.cssParentSelector/

  29. Okay look – I’ve not read through every comment, so I apologize if this has been said already: All I really need is the ability to locate, say, div.webform, but Only if it contains within it a form#someID. Now, I know this sounds very similar to div.webform:has(form#someID), but I don’t see that as the case.
    First, we tell the browser to find div.webform. Done! No extra overhead!
    Now, for each div.webform found, search within it for a form#someID! Done! Minimal extra overhead! What’s the problem there???
    Okay, so I don’t *know* how CSS works, internally, but it can’t really be much more complicated than that…can it???

  30. I saw a lot of questions about the use of :parent (or :has or <) selector.
    So I will say how I got here in a first place.

    I am rendering page with Ajax+Comet+Smarty+MVC and "now I have a table".
    I want the browser cursor to de default on table rows BUT I want a cursor to be pointer whenever there is a anchor or rel present in table data.

    It is possible to do this "trick" with jQuery and or with post-process DOM traversal but its almost immposible to give that given "table row" a class type, the stream is rendered in means of pipes and comes out AS IS.

    So I hope I gave you a example "where" this selector could be used.

    I tried something like


    table >tbody > tr * { cursor:pointer: }
    table >tbody > tr * > :not(:link):not(:visited) { cursor:default: }

    but it didn’t work “as expected” :)

  31. First, apologies for the digital necrophilia by resurrecting a dead topic; but my first thoughts on the idea were:

    Consider <a><p><img src="" alt="" /></p></a> with a img:parent { some-property: some-value; } CSS. Naturally, one would think that it would match the "a" element, when indeed it would actually target the "p" element.

    Just a thought

  32. I came here looking for a solution to append an element (pointer carat) to all tags that have an tag member denoting the option for expansion. Other than using JS to scrape through the entire nested menu listing, it would have been nice to do:
    $(‘ul:parent’).append(‘>’);

  33. As @a already pointed out:

    > Browsers don’t look for elements that match selector, they look for selectors that match the element.

    But you don’t have to scan ‘all’ child-elements, just one that is matching, and in my experiences only generated content exceed a certain amount of child elements(the amount that could cause performance issues), thus in most of the cases it means the same elements again and again.

    Of course if you generate 10000 instances of articles on a page and then append a footer on the same level and call footer:parent it will cause huge performance issues, but once you understand how it works (adding a note to the specs), you can easily adopt your markup to not to face such problems (separating the list of articles in a div, because this is what the div meant for in HTML5).

    And those who don’t get to the point to understand performance issues or those who can’t modify the (otherwise wrongly written) markup, will simply not going to use this selector, but just because the (unfortunately) majority of the developers out there (not you, you are the exception ;) ) have no idea about the internal operating of the system they use, doesn’t mean that a powerful tool should retain unavailable for those who know how to use it!

  34. please reference the W3C article http://www.w3.org/TR/2013/WD-selectors4-20130502/ , section 3.2. Determining the Subject of a Selector, discussing the parent selector and what is in the works with the ‘!’ preceeding the parent element. i.e. ol li.someclass { background-color: blue} vs. !ol li.someclass { background-color: red;}

    The subject of the first example is the li.somclass where as the subject of the second selector is the parent ‘ol’.

    its not exactly the answer that everyone is looking for but should help in certain scenarios.

  35. I cannot see the benefits of a :parent pseudo class, it just goes against the whole “cascade principle”. If you need to style a parent element, you should probably be applying a class directly to that parent element. I generally break my CSS up into small “widgets”, with a class on the containing element, that should make your CSS easy to manage and possibly faster to render.

    Maybe I’m being dense, if so, please explain a situation where the :parent pseudo class would be necessary (or make it easier) to achieve your desired results.

    Cheers!

  36. Why do we care if a programmer produces bad CSS which is “expensive” to render? That programmer could also very easily create an infinite loop in JS.

    We need E < F

    Thanks for protecting us from ourselves, oh mighty creators of standards, but it would be helpful if you could take time out of your busy schedules to stop helping. X0X0

Leave a Reply
Not required

CODE: Please escape code and wrap in <pre><code>, doing so will automatically syntax highlight