216

Is it possible to define a CSS style for an element, that is only applied if the matching element contains a specific element (as the direct child item)?

I think this is best explained using an example.

Note: I'm trying to style the parent element, depending on what child elements it contains.

<style>
  /* note this is invalid syntax. I'm using the non-existing
   ":containing" pseudo-class to show what I want to achieve. */
  div:containing div.a { border: solid 3px red; }
  div:containing div.b { border: solid 3px blue; }
</style>

<!-- the following div should have a red border because
     if contains a div with class="a" -->
<div>
  <div class="a"></div>
</div>

<!-- the following div should have a blue border -->
<div>
  <div class="b"></div>
</div>

Note 2: I know I can achieve this using javascript, but I just wondered whether this is possible using some unknown (to me) CSS features.

BoltClock
  • 630,065
  • 150
  • 1,295
  • 1,284
M4N
  • 90,223
  • 44
  • 210
  • 255
  • 1
    You might want to update the question to state, perhaps with blinking bold text, that you're trying to style the PARENT div, not its children. I know that info is in the question itself, but unless you want to get a ton of incorrect answers it's probably worth the effort. – Seth Petry-Johnson Feb 24 '10 at 14:15
  • Thanks @Seth. I tried to improve the question title and text. Please edit the question if you think it still unclear. – M4N Feb 24 '10 at 14:35
  • This is the perfect example of why this type of support in CSS would be ideal: `ol < li:nth-child(n+10) { margin-left: 2rem; } ol < li:nth-child(n+100) { margin-left: 3rem; } ol < li:nth-child(n+1000) { margin-left: 4rem; }` In an ideal world this would increase the margin of the `ol` dependent on the number of `li` children in contained so that the margin on the left would only be as wide as it needed to be. – Jonmark Weber Dec 08 '15 at 22:38

4 Answers4

131

As far as I'm aware, styling a parent element based on the child element is not an available feature of CSS. You'll likely need scripting for this.

It'd be wonderful if you could do something like div[div.a] or div:containing[div.a] as you said, but this isn't possible.

You may want to consider looking at jQuery. Its selectors work very well with 'containing' types. You can select the div, based on its child contents and then apply a CSS class to the parent all in one line.

If you use jQuery, something along the lines of this would may work (untested but the theory is there):

$('div:has(div.a)').css('border', '1px solid red');

or

$('div:has(div.a)').addClass('redBorder');

combined with a CSS class:

.redBorder
{
    border: 1px solid red;
}

Here's the documentation for the jQuery "has" selector.

Nisse Engström
  • 4,555
  • 22
  • 24
  • 38
KP.
  • 12,464
  • 3
  • 37
  • 60
  • 15
    Side note: for better performance, jQuery doc page for `:has` selector advises to use `.has()` method (http://api.jquery.com/has/) => applied to current question it would give for example `$('div').has('div.a').css('border', '1px solid red');` – Frosty Z Dec 30 '13 at 11:00
  • For this to work one would have to use mutation observer, to check wether the child changed it's state... – Legends Mar 09 '17 at 18:03
  • This doesn't truly answer the question which is "Is there a way to do this with CSS". It clearly states: "I know I can achieve this using javascript, but I just wondered whether this is possible using some unknown (to me) CSS features." – SunshinyDoyle Aug 28 '18 at 14:27
  • https://developer.mozilla.org/en-US/docs/Web/CSS/:has, I think this `:has` selector can work but it's in draft version and no browser supports it. – AliF50 Mar 03 '20 at 18:32
69

Basically, no. The following would be what you were after in theory:

div.a < div { border: solid 3px red; }

Unfortunately it doesn't exist.

There are a few write-ups along the lines of "why the hell not". A well fleshed out one by Shaun Inman is pretty good:

http://www.shauninman.com/archive/2008/05/05/css_qualified_selectors

Nisse Engström
  • 4,555
  • 22
  • 24
  • 38
Justin Wignall
  • 3,260
  • 18
  • 23
  • 52
    just an update 8 years later: there is still no support for this selector method. – Kraang Prime Oct 22 '18 at 10:59
  • 1
    There is a working draft for this feature in the form of a [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) pseudo-class, but we'll only get to use it in CSS4. As of now (late 2019), JS is still the only way to achieve this functionality. – zepp133 Dec 16 '19 at 07:38
  • I believe this won't enable css styles, it will still need javascript to use the selector. Unless this has changed? – Justin Wignall Dec 17 '19 at 13:32
  • 4
    9 years and do they think this feature is needless? – Loi Nguyen Huynh Dec 21 '19 at 00:27
1

On top of @kp's answer:

I'm dealing with this and in my case, I have to show a child element and correct the height of the parent object accordingly (auto-sizing is not working in a bootstrap header for some reason I don't have time to debug).

But instead of using javascript to modify the parent, I think I'll dynamically add a CSS class to the parent and CSS-selectively show the children accordingly. This will maintain the decisions in the logic and not based on a CSS state.

tl;dr; apply the a and b styles to the parent <div>, not the child (of course, not everyone will be able to do this. i.e. Angular components making decisions of their own).

<style>
  .parent            { height: 50px; }
  .parent div        { display: none; }
  .with-children     { height: 100px; }
  .with-children div { display: block; }
</style>

<div class="parent">
  <div>child</div>
</div>

<script>
  // to show the children
  $('.parent').addClass('with-children');
</script>
Mauricio Morales
  • 958
  • 1
  • 8
  • 15
0

In my case, I had to change the cell padding of an element that contained an input checkbox for a table that's being dynamically rendered with DataTables:

<td class="dt-center">
    <input class="a" name="constCheck" type="checkbox" checked="">
</td>

After implementing the following line code within the initComplete function I was able to produce the correct padding, which fixed the rows from being displayed with an abnormally large height

 $('tbody td:has(input.a)').css('padding', '0px');

Now, you can see that the correct styles are being applied to the parent element:

<td class=" dt-center" style="padding: 0px;">
    <input class="a" name="constCheck" type="checkbox" checked="">
</td>

Essentially, this answer is an extension of @KP's answer, but the more collaboration of implementing this the better. In summation, I hope this helps someone else because it works! Lastly, thank you so much @KP for leading me in the right direction!

miles
  • 1
  • 1