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.
- 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.
In order to select "the last 2
<span>
elements" you need a selector that can select both:- The last child (not descendant) element, but only if it's a
<span>
that's also preceded by another<span>
. - The second-to-last (not descendant) element, but only if it's a
<span>
element that's also immediately followed by another<span>
.
- The last child (not descendant) element, but only if it's a
Case 1 is straightforward: it's just
span span:last-child:last-of-type { }
orspan span:nth-last-child(1) { }
.Case 2, however, is impossible: CSS only has 2 sibling selectors (
x y
andx ~ y
), and both of which can only be used to select they
sibling, not thex
sibling.- There is the
:has()
pseudo-element function which you can use today, but only withquerySelector
/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) { }
- Once it is supported (and assuming it uses the
- As of early 2022, there is work being done to add support for a restricted form of
- There is the
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
andyellow
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 "sit" span because they cannot select a <span> followed by a <strong>:</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 "sit" 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>