Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS to select nearest descendent?

Using CSS, is there any way to select an element's nearest descendent that matches a certain selector?

<div class="foo"> <!-- if we're inside this -->
  <div>
    <br />
    <div class="bar"> <!-- match this -->
      <div class="bar"> <!-- but not this -->
        <div class="bar"> <!-- or this -->
        </div>
      </div>
    </div>
    <div class="bar"> <!-- I don't really care whether we match this -->
    </div>
  </div>
</div>

That is, something that would select the first .bar within any .foo (no matter how many child or sibling elements are in between), but not the second or third .bar.

like image 905
JW. Avatar asked Jun 08 '12 18:06

JW.


People also ask

What is the use of descendant selector in CSS?

The descendant selector in CSS is used to match all elements that are descendants of a specified element. You can try to run the following code to implement CSS Descendent Selector:

How to match two elements in a CSS selector?

The above syntax has two arguments the first Element says the initial element to match, the second element must be a descendant of first element. And the body statements follow the CSS Styles. And adding a small space between the two selectors the same style element is reflected in child element too.

How to use sibling selector in CSS?

The general sibling selector is explicitly implemented for choosing a group of elements allocated for the same parent element. Here is a sample example of how it can be implemented: This selector is implemented to select every child element within the particular tag mentioned in the CSS selector section.

How to target only the descendants of an element?

For example, in the HTML sample shown in Listing 3.1, three <a> elements are descendants of the <li> elements. To target these three <a> elements only, and not all other <a> elements, a descendant selector can be used as shown in Listing 3.6.


2 Answers

Here's my hacktastic answer: http://jsfiddle.net/thirtydot/gqJdP/2/

/*repeat for as many levels as there could be*/
.foo > .bar,
.foo > :not(.bar) > .bar,
.foo > :not(.bar) > :not(.bar) > .bar {
    border-color: blue;
}
.bar {
    border: 1px solid red;
    padding: 10px;
}

Quoting myself from a comment:

My suggestion is viable, although distasteful, if you can place an upper limit on the possible number of parents between .foo and the .bar you want to match. Even if you had to repeat the selector (let's say) 10 times, it's not too bad.

like image 135
thirtydot Avatar answered Sep 25 '22 00:09

thirtydot


No, there is no way to do what you are asking for completely using only CSS. You can select the nth-child or nth-of-type, but not the-nth-item-matching-this-selector.

As "proof", review the list of CSS3 selectors available (which covers all the capabilities of CSS1 and CSS2.1):
http://www.w3.org/TR/selectors/

You will need to use JavaScript (on the client) or server-side manipulation (e.g. annotating the first such item with a special class).


Edit: Based on your edit, you can do:

.foo .bar      { font-weight:bold; color:red }
.foo .bar .bar { font-weight:normal; color:black }

i.e. you need to explicitly "undo" the styling applied on ancestors that would be inherited. This is "fragile"—if you change a style on an ancestor you need to be sure to also change the value on the corresponding "override" rule—but effective.

And, if @thirtydot posts his hack as an answer, we can all accept that, too.

Although it probably does not help, note also that you can accomplish this goal with XPath.

like image 35
Phrogz Avatar answered Sep 26 '22 00:09

Phrogz