skip to Main Content

I need to target a descendant class (let’s call it inner) of an outer class, but only the first element with the inner class (which is not outer’s first child). An arbitrary number of elements and levels of nesting may appear prior to the first occurrence of inner inside outer, and after that inner may (re)appear multiple times. A simplified HTML structure follows (with my failed attempt to use :nth-child():

    <!DOCTYPE html>
    <html>
    <head>
    <title>Test</title>
    
        <style>
            .outer :nth-child(1 of .inner) {
                color:red;
            }
        </style>
    
    </head>
    <body>
    
    <div class="outer">
        <!-- Arbitrary number of elements may happen here -->
        <div>
            <!-- Arbitrary number of elements may happen here -->
            <div class="inner">
                <span>Should BE affected!</span>
            </div>
        </div>
        <!-- Arbitrary number of elements may happen here -->
        <div>
          <!-- Arbitrary number of elements may happen here -->
          <div>
            <!-- Arbitrary number of elements may happen here -->
            <div class="inner">
                <span>Should NOT be affected!</span>
            </div>
          </div>
        </div>
    </div>
    
    </body>
    </html>

In this example inner appears only twice, but it can appear multiple times.

I can’t control the HTML generated, all I can do is add the .outer class to the outer div (the inner class actually is added by the control rendering library I’m using, which I don’t control – no pun intended).

So I need to target this "first" inner element, and ignore all other instances of it. Am I approaching this the wrong (CSS) way? How can I achieve that?

3

Answers


  1. Basically, you cant. nth-child will always select from its own parent container not other parents or ancestors. You need JavaScript.

    jQuery has a process (.first) for your requirement

    $(".inner").first().css("background-color", "red");
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <div class="outer">
      <!-- Arbitrary number of elements may happen here -->
      <div>
        <!-- Arbitrary number of elements may happen here -->
        <div class="inner">
          <span>Should BE affected!</span>
        </div>
      </div>
      <!-- Arbitrary number of elements may happen here -->
      <div>
        <!-- Arbitrary number of elements may happen here -->
        <div>
          <!-- Arbitrary number of elements may happen here -->
          <div class="inner">
            <span>Should NOT be affected!</span>
          </div>
        </div>
      </div>
    </div>
    Login or Signup to reply.
  2. In CSS, there is currently no parent selector or a previous sibling selector that allows one to select an element based on a descendant. However, we can achieve what you’re looking for by using a combination of the :first-child and :not pseudo-classes.

    Here’s how we can do it:

    .outer :not(:first-child) > .inner {
        color: red;
    }

    This selector targets all elements with the class .inner that are direct children of an element with the class .outer, but only if they are not the first child of their parent. This effectively selects only the second occurrence of .inner and any subsequent occurrences, while excluding the first one.

    The second occurrence of .inner (inside the nested div) will be affected, and the first occurrence (inside the immediate child div) will be ignored.

    The :not pseudo-class is used to negate the :first-child condition, allowing to select only the elements we want.

    Login or Signup to reply.
  3. So, you’re selecting the .inner element that:

    • is contained in a .outer element
    • is not preceded by a sibling .inner element
    • is not preceded by a sibling element that contains a .inner element
    • doesn’t have an ancestor that’s preceded by a sibling .inner element
    • doesn’t have an ancestor that’s preceded by a sibling element that contains a .inner element

    I.e. .outer .inner:not(.inner ~ *):not(:has(.inner) ~ *):not(.inner ~ * *):not(:has(.inner) ~ * *)

        <!DOCTYPE html>
        <html>
        <head>
        <title>Test</title>
        
            <style>
                .outer .inner:not(.inner ~ *):not(:has(.inner) ~ *):not(.inner ~ * *):not(:has(.inner) ~ * *) {
                    color:red;
                }
            </style>
        
        </head>
        <body>
        
        <div class="outer">
            <!-- Arbitrary number of elements may happen here -->
            <div>
                <!-- Arbitrary number of elements may happen here -->
                <div class="inner">
                    <span>Should BE affected!</span>
                </div>
            </div>
            <!-- Arbitrary number of elements may happen here -->
            <div>
              <!-- Arbitrary number of elements may happen here -->
              <div>
                <!-- Arbitrary number of elements may happen here -->
                <div class="inner">
                    <span>Should NOT be affected!</span>
                </div>
              </div>
            </div>
        </div>
        
        </body>
        </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search