csscss-selectors

Select the first occurrence of a tag but igoring the hierarchy using CSS


I've stumbled upon this question about CSS selector. Which is to select the first occurrence of <span> but igoring its hierarchy. I know I could use JavaScript to document.querySelectorAll('span') to get the first DOM object and assign class to it. But is there a way to achieve it avoid using JavaScript?

The HTML structure is arbitrary, so selectors like div:first-child > p:first-of-type > div:first-of-type span:first-of-type is too specific. I want the selector works even when HTML structure is changed.

I have tried something like this, but I'm not sure why MISS ME 4 is also selected.

span:first-of-type:not(:has(span):nth-child(n+2 of :has(span)) span) {
  color: red;
  font-weight: bold;
}
<div>
  <h1>Select the first occurrence of span</h1>
  <p>
    <span>HIT ME</span>
    <div>
      <span>MISS ME 1</span>
      <span>MISS ME 2</span>
      <span>MISS ME 3</span>
    </div>
    <span>MISS ME 4</span>
    <span>MISS ME 5</span>
  </p>
  <p>
    <span>MISS ME 6</span>
    <span>MISS ME 7</span>
  </p>
</div>
<div>
  <p>
    <span>MISS ME 7</span>
    <span>MISS ME 8</span>
  </p>
  <p>
    <span>MISS ME 9</span>
    <span>MISS ME 10</span>
    <span>MISS ME 11</span>
  </p>
  <p>
    <span>MISS ME 12</span>
    <span>MISS ME 13</span>
    <span>MISS ME 14</span>
    <div>
      <span>MISS ME 15</span>
      <span>MISS ME 16</span>
      <span>MISS ME 17</span>
    </div>
  </p>
</div>

Is it even possible? I'm completely out of ideas.


Solution

  • To select the first span in a document select the span element that:

    Use:

    span:not(span ~ span):not(:has(span) ~ span):not(span span):not(span ~ * span):not(:has(span) ~ * span)
    

    span:not(span ~ span):not(:has(span) ~ span):not(span span):not(span ~ * span):not(:has(span) ~ * span) {
      color: red;
      font-weight: bold;
    }
    <div>
      <h1>Select the first occurrence of span</h1>
      <p>
        <span>HIT ME</span>
        <div>
          <span>MISS ME 1</span>
          <span>MISS ME 2</span>
          <span>MISS ME 3</span>
        </div>
        <span>MISS ME 4</span>
        <span>MISS ME 5</span>
      </p>
      <p>
        <span>MISS ME 6</span>
        <span>MISS ME 7</span>
      </p>
    </div>
    <div>
      <p>
        <span>MISS ME 7</span>
        <span>MISS ME 8</span>
      </p>
      <p>
        <span>MISS ME 9</span>
        <span>MISS ME 10</span>
        <span>MISS ME 11</span>
      </p>
      <p>
        <span>MISS ME 12</span>
        <span>MISS ME 13</span>
        <span>MISS ME 14</span>
        <div>
          <span>MISS ME 15</span>
          <span>MISS ME 16</span>
          <span>MISS ME 17</span>
        </div>
      </p>
    </div>