Universal Child Replacement

Published 13 years, 5 months ago

The other day I hit a situation that pushed me to come up with a way to simulate the child selector in a way Internet Explorer could understand using two rules.  I doubt I’m the first to think of it, but I’d never seen it before, so I thought I’d document the solution here.

The deal was that I had a column of text featuring white background with some black flecks in it.  On top of that went some near-black text.  All fine, except where the text sat on top of a fleck, which made it next to impossible to read.

To counteract that effect, I decided to set the background of the various descendants of that div to be white, so they’d mask any flecks they were overlapping.  Thus I wrote:

#main * {background: #FFF;}

It worked great for about a second.  That’s when I realized that I had links in the column, and some of them were sitting inside table rows with a non-white background.  The rule I’d just written was giving the links white backgrounds, which had the visual effect of punching holes in the row backgrounds.  That was no good.

What I really needed was a way to just set white background on elements that were children of #main.  CSS has a child selection combinator (>) but neither version of Internet Explorer supports it.  After a few moments’ thought, I realized that I could add a rule that would make transparent the background of any element that was at least a grandchild, but not a child, and it would still work in Explorer.

#main * {background: #FFF;}
#main * * {background: transparent;}

The end result is that there is a way to simulate child selection without actually using the child combinator.  The general pattern is to use a normal descendant selection in your first rule, and then “undo” the first rule with a second that has a universal selector in the middle.  Suppose you want to boldface any p element that’s a child of a div, but no others.  The solution:

div p {font-weight: bold;}
div * p {font-weight: normal;}

It might not be something you use every day, but if it’s needed, there you go.

Update: Lachlan points out that you’ll need to watch out for specificity conflicts when using this technique.

  • Published
  • Categorized under CSS
  • 35 responses so far

  1. Holy crap! Awesome!

  2. It’s so neat it needs a ribbon tied round it.

  3. Wow that’s very intuitive. It would definitely help a lot in terms of box layouts with a hierchy-like structure.

  4. You know, I think that’s absolutely brilliant…

  5. This is something I’ve been using for a while but never really thought about it, just thought it was just another CSS thing, and thus never blogged it myself.

    That said, however, I have never really looked into any depth, just used it, I suppose.

    Hope your having a great time in London, and (probably not) see you there on Sunday :P

  6. When using this technique, you must remember that this is that “undoing” rules can be very difficult when you have more than one rule that may be applying to your elements. CSS has no way to tell an element “ignore any other changes to this property”.

    If you have the following markup:

    <div id=”monkey”>
    <div class=”ape”>
    <p>Monkey Paragraph</p>
    <div>
    <p>Monkey Nested Paragraph</p>
    </div>
    </div>
    </div>
    <div>
    <div class=”ape”>
    <p>Normal Paragraph</p>
    <div>
    <p>Normal Nested Paragraph</p>
    </div>
    </div>
    </div>

    The CSS:

    #monkey { font-weight: bold }
    div > p { font-weight: bold }

    Will not be equivalent to:

    #monkey { font-weight: bold }
    div p { font-weight: bold }
    div * p { font-weight: normal }

    Normally, it’s easy enough to pump in enough extra rules to make it work, but don’t expect it to work like a real child selector.

  7. I did a similar thing when wrangling the CSS layout for a menu system built from UL/LI tags (outermost ). Basically:

    .menu, .menu ul { /* All menus absolutely positioned */ }
    .menu li { /* Float horizontally */ }
    .menu ul { /* Offset vertically */ }
    .menu ul li { /* Stack vertically */ }
    .menu ul ul { /* Offset horizontally */ }

    It’s an easy first-level horizontal menu with dropdowns underneath, and all subsequent levels consist of vertically stacked items in menus offset to the right, like a standard OS menubar.

    P.S. I’ve revised my PNG fix script, it didn’t correctly handle PNG images from imported stylesheets before, so if you want to try it again with S5 feel free :).

  8. Daniel: good point; these aren’t precisely equivalent, although I suspect that in 99% of cases, they’re close enough to be satisfactory.

    However, in the example that you give, I wouldn’t use a child selector at all, let alone try to replace it with the technique I described. Instead I would use a plain descendant selector (div p) since it more efficiently meets the given case.

  9. That is cool! and I know I am going to need it very soon THX

  10. Uh-oh, Eric – what very misleading title for a post that has nothing to do with universally replacing your children, which by the way, no-one should ever be searching Google for anyways :-)

  11. I thought that was always the obvious solution, I’ve used it several times before. However, it does get annoying because it can have unintended side-effects, thanks the the specificity of ‘the undo rule’, which may unintentionally override the styles of any descendant element.

  12. Could you explain what you mean, Lachlan? The specificity of the first rule and the second are precisely identical, which is why they’re written in that order (something I probably should have clarified in the post). So I’m being a bit dim, perhaps, but I’m not sure what you’re saying.

  13. @Chriztian lol! I was thinking along those lines.

  14. Eric,
    Say, for example, you have another rule in your stylesheet that sets the background and color of all <a> elements, together with your rules from above, like this:

    #main * {background: #FFF;}
    #main * * {background: transparent;}
    a { background-color: green; color: white; }

    Assume that were applied to this markup:

    <div id="main">
    <p>This is a <a href="#">test</a>.</p>
    </div>

    Because the specificity of the #main * * rule you provided is higher than that of this rule, any a element within that section will end up with a transparent background and white text. That also illustrates why it’s vitally important to always set a color whenever you set background-color.

    The easy way to fix this is to either increase the specificity of the problematic rules, or use !important. However, this result does not occur when using the child combinator like #main>* for the same purpose.

  15. Ah, I see. Thanks for the clarification. That’s definitely something to keep in mind when using this technique.

  16. Eric, a great solution to a tricky problem. With Lachlan’s warning, I’m sure a lot of people will find good use for this technique.

    Top notch!

  17. In fact an uncommon solution, so it’s good that you documented it.

  18. This is along the lines of a problem I have been working on for the last few months.

    Basicly, I wan’t to be able to set the base font size for an entire CMS based website using EM’s … I’m already using percentage font sizes on body to make fontsizes the same across multiple browsers.

    The rule
    p, td, th, div, span { font-size:0.8em }
    would seem to fit untill you relise that em’s are relative, so
    <div>Correct size <p>0.8 x correct size (aka Tiny!)</p></div>

    Hopefully I should be able to use this psudo child selector to actually get this to work.

    Something like adding
    p *, td *, th *, div *, span * { font-size:inherit }
    Though, I know that isnt going to work right….

  19. Would it work/be better to use inherit in place of transparent?

    In the case of text, it’s usually going to be transparent, but last thing you’d want to do is cause frustration down the road when you’re trying to do a 37signals highlight and the css is overriding the yellow. (or whatever, that’s just a random example)

  20. Damn why didn’t I think of that. Good work!

  21. Like Jeremy Arnold above I recently decided to make the same conversion from fixed and percentage widths to EMs. I, too, have had a hard time trying to set a standard font-size across my site using EMs. So what is the correct markup for this?

    Surely something so essential MUST have a common solution. Or should we perhaps be using EXs?

    I know there are many schools of thought in this regard but surely the experts must all agree on a few basic requirements.

  22. Jeremy, you don’t have to specify a font size for each and every element type. If you want to specify an overall font size, set it for body and that is all you need to do.

    Speaking as a user, why do you want to reduce overall font size anyway? I’ve got a perfectly good font size configured in my browser, if you mess with it, you make things less readable for me.

  23. Paul:

    body { font-size: [put your font size here]; }

    That is all you need, unless you want to mess with the font size of form elements, in which case also use something like:

    * { font-size: inherit; }

    Like I said above though, the most user-friendly approach is simply to leave the base font size alone and just specify the relative sizes of elements like headings.

  24. Took me a while, but I get it. This is pretty clever.

  25. One thing – beware of the use of the universal selector on constrained devices as they can cause significant performance problems to the UE – not much of an issue on a monster desktop gaming machine but germane for mobile & portable devices.

  26. Eric,

    I read this when you first posted it, and today found myself needing this solution, as Firefox was working beautifully but only because I was using child selectors.

    Wait a minute, I thought….Eric wrote a solution to this recently. And sure enough, it worked out great.

    Thanks!

  27. this is an interesting behaviour. However, it does not replace the child selector in cases where you need to reach the first element after another, in the same container.
    i.e, styling ul when after h2:

    <div id="#content">
    <h2>title</h2>
    <ul>
    <li>item</li>
    </ul>
    </div>

  28. Sorry, I had in mind adjacent sibling selectors (+) and mistakengly searched for a workaround in IE for child selectors. Eric, if you manage to devise a workaround for sibling selectors, we’d all be most grateful!

  29. […] ont-weight: bold;} div * p {font-weight: normal;} I saw this trick for the first time on Meyerweb, so that’s where the credit […]

  30. Amazing! You saved me hours. =)

  31. […] t reading (13/6/05)

    June 13th, 2005
    The ways to style visited links Universal Child Replacement Scrolling drop shadows Building a page te […]

  32. hi eric,
    may be you know how to solve a problem with one child and many daddies

    can you reduce this?
    #thistory .subnav A, #whistory .subnav A

  33. AFAICS This does not deal with tree controls. Well only to a certain depth :(

    I wrote a tree control that only works on FF, Opera, Safari, and Google. But to find it did not work on IE ! :(

    Have to rewrite and refactor the JavaScript !

  34. […] I have been referring myself to Eric Meyer’s Universal Child Replacement Technique. […]

  35. Eric,

    I’ve documented a few other methods for emulating child selectors (not universally) here: http://craftycode.wordpress.com/2010/05/19/emulating-css-child-selectors-in-ie6/

    This includes a little trick to deal with nested classes, too.

Leave a Comment

Management reserves the right to edit or remove any comment, especially when abusive or irrelevant to the topic at hand. HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <em> <i> <q cite=""> <s> <strong> <pre class=""> <kbd>


Comment Preview

If you're satisfied with what you've written, then go ahead...