3

EDIT: This question is along a similar vein to my own, but it's not quite a duplicate because I understand how the pseudo-selectors of :first-child and :first-of-type actually work. The question is not "Why are my selectors not working," but rather "What selector besides just plain :first-child can I use for my circumstance?" The accepted answer of that question just explains information I already know.

===

In JavaScript, when you use element.querySelector('a'), it will return the first <a> tag it finds inside the element, no matter how deep, and it will not continue searching for more elements.

In CSS, you can use a space like .element a, and it will apply to every single <a> tag it finds inside the element, no matter how deep. This is more similar to element.querySelectorAll('a') if you were to compare it to JavaScript.

Even .element > a applies to every <a> tag that is a direct child of the element, still not quite right and I'd rather not depend on the nesting level. .element a:first-child is closer, but still not perfect. This will still apply to multiple elements if your structure looks like this:

<span class="element">
  <span>
    <a>Demo</a> <!-- I only want to select this one -->
    <a>Demo</a>
  </span>
  <span>
    <a>Demo</a>
    <a>Demo</a>
  </span>
</span>

In the example above, you could hypothetically use .element > span:first-child > a:first-child, but that becomes extremely rigid and heavily depends on the nesting structure, which in my case changes depending on the place it appears in the document. Normally I would adjust the HTML to have a more consistent and easier structure, but I don't have that freedom at this time, I just need to work with what's available.

Is there some kind of CSS magic I can use to select only the first match, regardless of depth? Or will I just plain need additional classes?

  • 1
    This other question doesn't use the expression "first matched element", but it's similar in principle: https://stackoverflow.com/questions/27524415/matching-the-first-nth-element-of-a-certain-type-in-the-entire-document – BoltClock Oct 03 '19 at 16:59
  • Thanks guys! So far it seems like my question hasn't -quite- been answered before, but Stack Overflow is a big place, lol. Regardless, I think I found a pretty satisfying answer, and I hope it helps anyone else searching for this. –  Oct 03 '19 at 17:47

1 Answers1

3

There is not a single selector that will cover that.

So you have pretty much two choices, either add a class, or use this trick, where you CAN apply styling in a way that will visually appear to affect only the element(s) you care about.

The idea is simply to get it half-right with the first selector .element a:first-of-type and then override/restore the ones that were incorrectly matched with a second selector .element :not(:first-child) a - this will also match <a> tags that were not first-of-type, so it seems like a good place to put your default styling along with .element a, to cover the rest.

.element {
  display: block;
}

/* first <a> tags to appear at any level turn blue */
.element a:first-of-type {
  color: blue;
}

/* "restore" styling on <a> tags with any parent that isn't the first child */
/* also, set default styling in one place */
.element :not(:first-child) a,
.element a {
  color: black;
}
<span class="element">
  <span>
    <a>Demo</a> <!-- Select this one -->
    <a>Demo</a>
  </span>
  <span>
    <a>Demo</a>
    <a>Demo</a>
  </span>
</span>

<span class="element">
  <span>
    <span>
      <a>Demo</a> <!-- Select this one, further nested -->
      <a>Demo</a>
    </span>
  </span>
  <span>
    <a>Demo</a>
    <a>Demo</a>
  </span>
</span>
Asons
  • 84,923
  • 12
  • 110
  • 165
BCDeWitt
  • 4,540
  • 2
  • 21
  • 34
  • Maybe someday they'll add something like `:first-match` to the spec, but until then, this is perfect. Thanks! –  Oct 03 '19 at 17:13
  • 1
    @JonDeWitt and BDawg -- Yepp, there was a 3rd way :) ... do note though, with this you can't use a global rule like `a { color: green }`, as the above reset trick will override it, no matter where you position that rule, having lower specificity. – Asons Oct 03 '19 at 17:21
  • Yeah, I kinda figured no answer would be perfect, due to css selector hell, lol. But, until my freedom to modify the HTML becomes a little more open, I gotta make it work somehow. That said, I like the idea of just using that rule as the default, instead of "reverting" it to the default. –  Oct 03 '19 at 17:26
  • After some thinking, I found yours better than mine, so I moved a part of mine to yours, gave it an upvote and delete mine. – Asons Oct 05 '19 at 13:52