Home > Software engineering >  Add styles for only last two spans in element
Add styles for only last two spans in element

Time:02-24

Hi I have this code and each word is wrapped into span tag and I want to add styles only to the last two spans, even if <p> tag has <em> tag inside and it has <span> tags the condition should be the same, the last two spans in a <p> tag.

P.S. Without em tag it's working normally

.st span:nth-last-child(-n   2) {
  background-color: yellow;
}
<p >
  <span>With</span>
  <span>ipsum</span>
  <em>
    <span>dolor</span>
    <span>sit</span>
  </em>
  <span>amet,</span> consectetur adipisicing elit.
</p>

<p >
  <span>Lorem</span>
  <span>ipsum</span>
  <em>
    <span>dolor</span>
    <span>sit</span>
  </em>
  <span>amet,</span>
  <span>consectetur</span>
  <span>adipisicing</span>
  <span>elit.</span>
</p>

Should work without em tag or maybe another tag can be instead

CodePudding user response:

Check Now

target CSS This way

.st > span:nth-last-child(-n   2)

https://codesandbox.io/embed/aged-violet-5w8gyt?fontsize=14&hidenavigation=1&theme=dark

CodePudding user response:

Your post is unclear about how <span> elements inside an immediate p.st > em element should be selected - my answer assumes you only want to select the last sibling pair of <span> elements if and only if the sibling pair is also the last children (not descendants) of their parent p.st.


  • What you're asking is (currently, as of February 2022) impossible to achieve in CSS (without JavaScript) in the general case.

    • This is because CSS selectors can only select elements based on a very restricted set of element properties, or based on an element's ancestors, and an element's previous siblings.
      • But a selector cannot select elements based on future siblings nor descendants of other ancestors.
  • In order to select "the last 2 <span> elements" you need a selector that can select both:

    1. The last child (not descendant) element, but only if it's a <span> that's also preceded by another <span>.
    2. The second-to-last (not descendant) element, but only if it's a <span> element that's also immediately followed by another <span>.
  • Case 1 is straightforward: it's just span span:last-child:last-of-type { } or span span:nth-last-child(1) { }.

  • Case 2, however, is impossible: CSS only has 2 sibling selectors (x y and x ~ y), and both of which can only be used to select the y sibling, not the x sibling.

    • There is the :has() pseudo-element function which you can use today, but only with querySelector/querySelectorAll: it cannot be used in CSS rules.
      • As of early 2022, there is work being done to add support for a restricted form of :has() into Chromium, and it's exciting stuff, but don't hold your breath - I'm not expecting to see it go mainstream for at least another couple of years.
        • Once it is supported (and assuming it uses the :scope keyword) then you could use this:
          p.st:has( :scope > span   span:last-child:last-of-type ) > span   span:last-child:last-of-type,
          p.st:has( :scope > span   span:last-child:last-of-type ) > span:nth-last-child(2) { }
          

So this is the best we can do, right now:

  • The selector below will incorrectly match the "amet" span in the first paragraph.
  • I changed the colors to red and yellow so you can see which rule is selecting which element specifically.

div {
  border: 1px solid black;
  margin: 1em;
}

div > p:first-child {
  font-size: 80%;
  color: #666;
}

p.st > span   span:nth-last-child(1) {
  background-color: red;
}

p.st > span:nth-last-child(2) {
  background-color: yellow;
}
<div>
  <p>Does not match any span elements: even though the last element is a span, that element doesn't have an immediate span sibling:</p>
  <p >
    <span>Lorem</span>
    <span>ipsum</span>
    <em>
        <span>dolor</span>
        <span>sit</span>
      </em>
    <span>amet,</span> consectetur adipisicing elit.
  </p>
</div>

<div>
  <p>The style rules incorrectly select the &quot;sit&quot; span because they cannot select a &lt;span&gt; followed by a &lt;strong&gt;:</p>
  <p >
    <span>Lorem</span>
    <span>ipsum</span>
    <strong>dolor</strong>
    <span>sit</span>
    <strong>consectetur</strong> adipisicing elit.
  </p>

</div>

<div>
  <p>In this case, the style rule does correctly select the last span sibling pair:</p>
  <p >
    <span>Lorem</span>
    <span>ipsum</span>
    <em>
        <span>dolor</span>
        <span>sit</span>
      </em>
    <span>amet,</span>
    <span>consectetur</span>
    <span>adipisicing</span>
    <span>elit.</span>
  </p>
</div>


However, while we cannot solve this in the general-case, it is possible to abuse :not() and other selector functions to prevent selecting elements that match other patterns. This approach is only feasible when you have control over the HTML and know that the HTML will always have a particular structure or pattern, in which case we can do this:

div {
  border: 1px solid black;
  margin: 1em;
}

div > p:first-child {
  font-size: 80%;
  color: #666;
}

p.st > span   span:nth-last-child(1) {
  background-color: red;
}

p.st > span:nth-last-child(2):not(strong   span) {
  background-color: yellow;
}
<div>
  <p>Does not match any span elements: even though the last element is a span, that element doesn't have an immediate span sibling:</p>
  <p >
    <span>Lorem</span>
    <span>ipsum</span>
    <em>
        <span>dolor</span>
        <span>sit</span>
      </em>
    <span>amet,</span> consectetur adipisicing elit.
  </p>
</div>

<div>
  <p>The style rules specifically exclude the case of <code>strong   span</code>, so &quot;sit&quot; is not selected anymore, but this requires every special-case to be excluded, which is only possible if you can make guarantees about the HTML's structure.</p>
  <p >
    <span>Lorem</span>
    <span>ipsum</span>
    <strong>dolor</strong>
    <span>sit</span>
    <strong>consectetur</strong> adipisicing elit.
  </p>

</div>

<div>
  <p>In this case, the style rule does correctly select the last span sibling pair:</p>
  <p >
    <span>Lorem</span>
    <span>ipsum</span>
    <em>
        <span>dolor</span>
        <span>sit</span>
      </em>
    <span>amet,</span>
    <span>consectetur</span>
    <span>adipisicing</span>
    <span>elit.</span>
  </p>
</div>

  • Related