Grow your CSS skills. Land your dream job.

Child and Sibling Selectors

Published by Chris Coyier

Do you know what the difference between these selectors are?

ul li { margin: 0 0 5px 0; }
ul > li { margin: 0 0 5px 0; }

I'll admit it took me longer than it probably should have (way back when) when I was learning the basics of CSS. In both cases, they are selecting list items that are children of unordered lists. But there is a difference between children and descendants.

The first selector above is a decendant selector. It will select any list items that are anywhere underneath an unordered list in the markup structure. The list item could be buried three levels deep within other nested lists, and this selector will still match it. The second selector above is a child combinator selector. This means it will only select list items that are direct children of an unordered list. In otherwords, it only looks one level down the markup structure, no deeper. So if there was another unordered list nested deeper, the list item children of it will not be targeted by this selector.

I think everyone understands the basic decendent selector, but let's do a quick overview of the other selectors in this style: the child combinator, the adjacent sibling combinator, and the general sibling combinator.

Child combinator

This one we covered in the intro to this article. Let's drive that same example home with a visual:

I think the nested list is a perfect example of why this selector is useful. It can be thought of as a way to prevent styling from cascading down further than you would like it to. Maybe you want to make the outer-most list items large and header-like, but the nested lists smaller and more body-copy like. Using a child combinator you can select only those top level list items and not worry about the large/header styling cascading down to the nested lists and having to fight against that styling.

Adjacent sibling combinator

An adjacent sibling combinator selector allows you to select an element that is directly after another specific element.

p + p { font-size: smaller; } /* Selects all paragraphs that follow another paragraph */
#title + ul { margin-top: 0; } /* Selects an unordered list that directly follows the element with ID title */

These selectors can help you apply styling in a contextual way. Perhaps you have a bunch of articles on a site. Most of them start with <p> elements and it looks great. But some of them start with a <ul>, which have top and bottom margin on your site to look good within an article. But when they start an article it causes an awkward gap. The latter selector above would zero out that top margin when it begins an article (immediately succeeds a title).

Hopefully helpful graphic:

General sibling combinator

The general sibling combinator selector is very similar to the adjacent sibling combinator selector we just looked at. The difference is that that the element being selected doesn't need immediately succeed the first element, but can appear anywhere after it.

If we use the same example structure as above, the last <p> element will be selected by p ~ p as well, because it is preceded by another <p> element, even though not directly.

Note that in both the general sibling and adjacent sibling selectors the logic takes place within the same parent element. That's what siblings means... sharing the same parent. In the graphical examples above, that's what the wrapping <div> is there for. If there was another <p> element after that <div>, it would still be selected by both div ~ p and div + p though, as it would be a sibling and an adjacent sibling to that <div>.

Browser Support

These are all good-to-go in IE 8 and up and all other modern browsers. IE 7 also has support, but be aware that HTML comments can screw them up and cause them to not match when in between siblings.

If you need deeper support than that, the ie7-js project (now updated all the way to ie9.js) will get you support for all of this back to even IE 5.5 with a single simple JavaScript file inclusion.

Wish upon a star

I've said it many times before, but one of the major missing selector styles in CSS is some kind of "contains" (or "has" or "qualified" or whatever you want to call it. The idea being like "select all paragraphs that contain images".

It could be like this:

p < img { width: 150%; } 

Apparently the powers that be have rejected it a number of times for some complicated reasons (speed related, I think). jQuery can do it:

$("p:has(img)")

And can even be adjusted to use the "<" syntax:

jQuery.parse.push(/^\s*(<)(\s*)(.*)$/);
jQuery.expr["<"] = jQuery.expr[":"].has;

The Spec

Doesn't hurt to read over the spec for this stuff either.

Comments

  1. Bert de Vries
    Permalink to comment#

    Hi Chris,
    I did knew about the “>”, the others are new to me. Thanks very much…

    ps. I keep wondering where you find the time to do all the stuff you do :)

    Gr. Bert

    • Chris doesn’t sleep. He just might be an alien from another planet.

    • ofer
      Permalink to comment#

      I Cant believe that only now i came across that perfect post.
      After working with css for a very long time, it’s good to get rid of old habits and learn something new :)

      Thanks a lot!!

  2. Eddy
    Permalink to comment#

    Chris, thanks for the article; as usual: perfect timing! Have a look at a blog post I made on DynamicDrive a couple of weeks ago: CSS selector wish list

  3. Permalink to comment#

    They are cool selectors and especially useful for styling menu’s. Or highlighting the first item in a list. Wouldn’t build me layout on them. One change in the HTML could easily screw things up.

  4. Adobe launched dreamweaver cs5 with a lot of features and one of them is css instection mode which will handle css properties.
    My question is that, will it be possible to fix CSS properties without knowing them in detail using dreamweaver cs5 . I have collected other major features at a post here Dreamweaver CS5 features for CSS properties and some others new features

  5. Permalink to comment#

    In my opinion, IE6’s single greatest sin is not having support for these selectors. They’re immensely useful and almost no one ever bothers with them.

    • eternicode
      Permalink to comment#

      IE8’s greatest sin, maybe, but not IE6’s.

      And even talking about IE8, I’m not convinced it’s bad. Undesirable, yes, but acceptable when you look at the big picture.

      IE7 was (afaik) mainly a feature catch-up session.
      IE8 was a css catch-up session, their attempt to fully support CSS2, and almost passes acid2.
      IE9 seems like it’s going to have impressive css3 support and html5 support, as well as support for hardware acceleration (though it’s debatable why this is needed).

      So, while IE isn’t moving as fast as we all would have liked it to, it *is* coming along.

  6. Ahh, that makes sense to me now.

  7. Permalink to comment#

    no more ul li ul li ul li ul li {} !
    thanks a lot Chris, your posts are always useful

  8. Permalink to comment#

    It was a pretty new for me. Thanks!

  9. Blue
    Permalink to comment#

    The images in this post are a really neat way to explain exactly how each selector works.

    As a quick aside, Why is there no “powered by: WordPress” in the new “shoutouts section… I think it’d be a perfect addition, and fill out the area quite nicely.

  10. Leonardo
    Permalink to comment#

    Please correct me if I’m wrong, but I think this is not right.
    ul>li will target any li that is a child of a ul.

    In your example you nested an OL, but change it to an UL (as you mention in the text) and the Nested Item 1 and 2 will also be targeted.

    • The example might be a little weird because some things might cascade down the list anyway, since a nested list is a child of that top level list item… But the child selector still doesn’t actually select the lower-level list items.

      That sounds like gibberish kind of… Here is an example where the inner OL has a color of black, then the parent list items have a color of red. The list item selector is more specific, but it doesn’t select the OL or the OL LI’s, so the color remains black.

      http://cdpn.io/qmGck

    • Leonardo
      Permalink to comment#

      I get the example of the link:
      the ol is black, so the bullet and the word “test” will be black. However the ol is enclosed by li tags so the entire ol becomes an li which is the child of the ul and with the style ul>li color red the ol will be red which means the bullet is red. OK clear.

      In your example the ol is enclosed by the li tags from List Item 2, as a result the whole ol will be styled in the same fashion as the ul>li.

      If you take the ol out of the afore mentioned li tags then Nested Item 1 and 2 will no longer be affected because they are children of the ol.

      If you change the ol into ul you get a parent ul with 3 children (3 li) of which 1 has 1 child (the second ul which inturn has 2 children, the Nested Items.

      OK so now ul>li will target the 3 li that are children of the top ul but also the 2 nested li from the nested ul…

      (OK now my head hurts. Looking forward to your response)

    • Yes I think everything I understood there is correct =)

      Are you worried that using this would select all the list items in nested lists anyway (not help “stop cascading” like I mentioned?).

      If that’s the case, I think in general usage this would be combined with, for example, and ID selector. So like ul#main-navigation > li would only select those top level LI’s without selected other nested LI’s

    • Leonardo
      Permalink to comment#

      Yes an ID would do the trick but this brings me back to me initial point.

      Let’s say (in your example) you want the font color of the content of the 3 top li’s (so the text List Item One, Two and Three) to be red.
      If you use ul>li not only will the 3 li become red but also the entire ol (bullets and text Nested Item One and Two.
      If you take the ol out of the 2nd li (with content List Item Two) then indeed only the 3 main li’s will be red, but only because the nested li’s are children of a ol.

      Change this ol into ul and the content (text) of the 3 main li’s AND the content (text) of the 2 nested li’s will be red.

      So the cascade doesn’t stop at the first level, but goes through the whole thing and will hunt down each and every li that is a child of a ul, no matter how deep is sits.

    • @Leonardo: At that point, id’s, classes, or more context is your only option.

      #nav > ul > li
      .article > ul > li
      div > ul > li

      That’s what you asked, from what I understand.

    • Chris could just have written this:
      ul { color: red; }
      ol { color: black; }

      But I think this language is what is confusing:
      “In otherwords, it only looks one level down the markup structure, no deeper”

      Which is not correct. I just tested this. Without any other styling (yes i see your jsbin Chris, and you are styling the ol), it indeed DOES go deeper. It will hit every li within that ul, no matter if the you use an id/class or not.

      You would have to style the ol in order to get the nested li’s to change color (which is what Chris has done in the jsbin thing)

    • Eddy
      Permalink to comment#

      Yeah, even though the li’s in the ol are not targeted, they inherit the rules that the targeted li in the ul gets. A better example would be:

      ul>li>a {}


      <ul>
      <li><a>one</a></li>
      <li>
      <ol>
      <li><a>two</a></li>
      </ol>
      </li>
      </ul>

      That would only target “one”.

  11. Thanks for this Chris!
    You mention it being supported across modern browsers, is it all good with safari and firefox then?

    This could be really useful when styling lists in certain ways, I’m forever adding classes to items to make them appear different but using the technique shown this won’t be an issue.

    But something Leonardo mentioned above me about the ul>li targeting any li that is a child of a ul, does this mean you will have to have instead of using another ? Hope I’m making sense here! But if you were to us a within a and use ul>li this will surely still style the inner ul li’s?

    Maybe someone else can put that into better English for me!

    Thanks

  12. amidude
    Permalink to comment#

    Very timely and informative post. (Hope they’ll let you trade your MBP. :-) )

  13. Permalink to comment#

    Awesome post! Sure this is going to save some line of code but still we have long way to get it standard fortunately …

  14. Permalink to comment#

    Check out this interactive jQuery selector tester, with which you can test your expressions against your html.

  15. Thanks Chris useful insight

  16. I really wish there was a “parent” selector. So you could select the container of an item. This would be especially useful when you’re not sure what container the content will be in, but you need to add a style to that container.

  17. Anil
    Permalink to comment#

    Great thanks for the help but tell me is this work on all the browsers….

    • Permalink to comment#

      Chris said:
      “These are all good-to-go in IE 8 and up and all other modern browsers. IE 7 also has support, but be aware that HTML comments can screw them up and cause them to not match when in between siblings.”

      Thanks for the article Chris, it’s very informative.

  18. I knew about >, and use it regularly, but I’ve never heard of + and ~. They seem like they could be very useful! Thanks for the tip.

  19. Awesome article – these selectors always gave me trouble before, but it makes much more sense now.

  20. My favourite use of + is is something like this:
    p{margin:0}
    p + p{margin: 30px 0 0 0}

    Or even nicer:
    dd + dt {margin: 30px 0 0 0}

    for > it’s especially nice in html5 where divs with classes and id’s can be replaced with nice headers and footers.
    body > header{color:white;}
    article > header{color:blue;}

    Actually, the amount of useful examples is endless.

    I’ve read these selectors do make your css slower. But I doubt anyone can see the difference between fast css and fast css(css is always fast)

  21. Permalink to comment#

    Cool, I never knew about the adjacent sibling selector, that is quite useful for avoiding large CSS files full of contextual rules.

  22. sampada
    Permalink to comment#

    Hello Chris, this article was little confusing. It will be really great if you can narrate it in much simpler way.

  23. Permalink to comment#

    thanks for such nice css tricks.

    I really did not know about > , + and ~ .

    Thanks and Keep this good work up.

    :)

  24. viktor
    Permalink to comment#

    this is 6 years old..

  25. Took me a while to using >, I’ll try to start to use + more, although I use some of the others with attribute selectors.

  26. nice post. thanks for all the efforts.

  27. zzzzBov
    Permalink to comment#

    the reason why you can’t have a “parent of” selector is due to a restriction of CSS to use a once-over algorithm.

    When a browser parses css selectors, it can apply the styles to each element by iterating through each element exactly once. if parent selectors were allowed, the parser would need to travel back up the xml tree, which is exponentially slower.

  28. vinod
    Permalink to comment#

    All this stuff that you can do with the help of advanced selectors is just so overwhelming. As you recommended ie7.project, a few questions popped into my head.

    1. Why are people still afraid of using such scripts?
    2. What drastic effect these scripts could possibly cause to your website?
    3. Is it a genuine/right way to get the browser to do what you want?

    I am really confused

  29. Pretty amazing! This article really help us. And the ie7-js project is very usefull too. I’m starting to test it here. Thank for for the efforts.

  30. this article AS ALWAYS thought me something that I’ve started to use immediately at a recent project, thanks again Chris !!!

  31. That’s an very very helpful article thank you very very much!

  32. “I’ve said it many times before, but one of the major missing selector styles in CSS is some kind of “contains” (or “has” or “qualified” or whatever you want to call it.”

    I haven’t looked into this subject in any detail, but I understand that would go against the concept of the cascade itself, and thus isn’t on the cards. I’ve used jQuery to select anchors which contain images (to take away a border-bottom).

    • Loic
      Permalink to comment#

      Indeed, the ” all divs but with a filter on the ID attribute
      div.myClass => all divs but with a filter on the class atribute
      a[title] => all links but with a filter on the title attribute presence

      So we can easily imagine something similar to filter on the content :
      p[ all paragraphs but with a filter on an img presence

  33. Chad
    Permalink to comment#

    Thanks Chris! I’ve seen these around a lot but admittedly have been lazy and never took the time to understand what the heck they were doing. These will be very useful going forward. I’ll likely try them out on my new site.

  34. Permalink to comment#

    I’ve had mixed success using child selectors, primarily due to IE6 and IE7 issues.

    I tried the IE7.js and IE8.js but they both seemed somewhat buggy at the time and caused even more erratic behaviour after inclusion.

    For some reason it was completely unable to handle certain absolutely and relatively positioned elements resulting in blocks of html not being displayed at all in IE6 for no apparent reason. I ended up having to discard the JS inclusion and use a conditionally included ie6.css file instead.

  35. Ant

    When I don’t have to make css for ie6, I always use those selectors. Because they are making code easier to read (both css and html).

  36. Permalink to comment#

    I know I’m late to the ballgame here… But I’ve created a quick post which illustrates a few other fancy css3 selectors that Chris doesn’t have listed here.

  37. Thanks! :D
    This was very helpful, I couldn’t figure it out myself, and I didn’t have time to test either.

  38. I was wondering, why there is adjacent sibling operator, and there is no nth-sibling selector?

    For example lets consider this situation:

    <div>
        <p></p>
        <p></p>
        <p></p>
        <div class="only-after-me"></div>
        <p></p>
        <p></p>
        <p></p>
        <div></div>
        <p></p>
        <p></p>
        <p></p>
        <div class="only-after-me"></div>
        <p></p>
        <p></p>
        <p></p>
    </div>
    

    So what if you want to select only middle p but only after the div.only-after-me (this is currently impossible as I know)?

    It would be something like this (or I would like to hear suggestion):

    div.only-after-me + p:nth-sibling(2)

    Where just using p:nth-sibling(2) would select every second p that comes after any element.

    This does not violate right to left rendering and memory issue, so Im rather curious if this was suggested before to the CSS draft and why it was rejected, and if it was not suggested, why not?

    Chris, I highly appreciate your work, and would like to hear your opinion and discuss on this topic.

    Thanx

  39. Ryan Tobin
    Permalink to comment#

    Those diagrams are so key for learning the difference between the selectors, it can be very confusing to explain.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".