0

I'm trying to style first and last child, inside span with a specific class. It doesn't matter if I use > or not in my CSS, This code won't work unless I remove the first and the last DIV, even though they are unrelated to the class I'm trying to target.

Why?

CSS:

.parent-span .cta-wrapper {background:gray;}
.parent-span:last-child > .cta-wrapper {background:red;   } 
.parent-span:first-child > .cta-wrapper {background:green}

HTML:

<div>xxxxxxxxx</div>

<span class="parent-span">
    <div class="cta-wrapper">
        <div>1</div>
    </div>
</span>


<span class="parent-span">
    <div class="cta-wrapper">
        <div>2</div>
    </div>
</span>      


<span class="parent-span">
    <div class="cta-wrapper">
        <div>3</div>
    </div>
</span>


<div>xxxxxxxxx</div>
Johannes
  • 64,305
  • 18
  • 73
  • 130
rockyraw
  • 1,125
  • 2
  • 15
  • 36

3 Answers3

1

nth CSS Selectors

When using nth CSS selectors, we must take certain things into consideration:

Hint #1 - The ancestor element that all of the target elements have in common.

Hint #2 - Each of the target elements' tagNames and their ancestors as well.

Hint #3 - The nth point of entry which isn't always the target elements. It could possibly                be more than one level depending if the target element and/or ancestor elements                have sibling elements or not and how they are positioned.

Hierarchy

The layout is a 5 level hierarchy.

  1. ROOT ---- tagName: <html>
  2. LEVEL 0 - tagName: <head>, <body> - Role: <<<COMMON ANCESTOR>>>
  3. LEVEL 1 - tagName: <div>, <span> -- Role: <<<POINT OF ENTRY>>>
  4. LEVEL 2 - tagName: <div> -------------- Role: <<<TARGET ELEMENT>>>
  5. LEVEL 3 - tagName: <div>

Two nth's - nth-child and nth-of-type

nth-child:

  • If you use nth-child, ignore hint #2,

  • So at the point of entry the common ancestor (i.e. <body>) has 5 children, not 3 children.

  • The line-up is: <div>, <span>, <span>, <span>, <div>

  • So instead of first-child it should be nth-child(2)

  • Instead of last-child, it's nth-child(4)

nth-of-type:

  • If you use nth-of-type, Hint #2 is key to understanding how to use nth-of-type.

  • At the point of entry the common ancestor has 2 <div>s and 3 <span>s.

  • Now we can specify a <span> as first, last, etc. because nth-of-type differentiates between element tagNames.

  • [common ancestor]................:body

  • {direct descendant(a.k.a. child)}:>
  • [point of entry].................: span:first-of-type
  • [target element].................: div
  • body>span:first-of-type div

Of course the selector above can have variations, but the important part is the point of entry span:first-of-type.

Demo

Click any span and it will revert from display:block to display:inline which is what was posted in OP

Hover over any element to see its Level in the hierarchy, its tagName, and what its role is.

There is a version of nth-child styles commented out included as well.*

// For demonstration purposes
Array.from(document.querySelectorAll('span')).forEach(function(spn, idx) {
  spn.addEventListener('click', function(e) {
    spn.classList.toggle('inline');
  }, false);
});
:root::before {
  content: 'ROOT';
}

html {
  height: 111vh;
  width: 90vw;
  background: rgba(255, 200, 50, 0.2);
  font: 600 15px/1 Consolas;
  cursor: crosshair;
  overflow-y: scroll;
  text-align: center;
  color: black;
}

body {
  height: 101%;
  width: 50vw;
  padding: 0 10vw;
  margin: 2vh auto;
  outline: 0.5rem solid rgba(250, 150, 150, 0.9);
  background: rgba(50, 255, 0, 0.4);
  font-size: 0.8rem;
}

div {
  max-height: 15vh;
  max-width: 50vw;
  padding: 10px;
  outline: 2px dashed darkblue;
  background: rgba(255, 200, 50, 0.5);
}

span {
  /* OP: display:inline */
  display: block;
  min-height: 10%;
  max-width: 50vw;
  padding: 0 20px;
  margin: 8px auto;
  outline: 3px solid blue;
  background: rgba(255, 0, 100, 0.7);
}

.inline {
  display: inline;
}


/*::..BEGIN DISABLED OP..::
.parent-span .cta-wrapper {background:gray;}
.parent-span:last-child > .cta-wrapper {background:red;   } 
.parent-span:first-child > .cta-wrapper {background:green}
}
::..END DISABLED OP..::*/

/*::..BEGIN nth-type-of..::*/
body>span>div {
  background: gray;
}

body>span:first-of-type>div {
  background: red;
}

body>span:last-of-type>div {
  background: green;
}
/*::..END nth-of-type..::*/

/*::..BEGIN nth-child..::
body>span>div {
  background: gray;
}

body>span:nth-child(2)>div {
  background: red;
}

body>span:nth-child(4)>div {
  background: green;
}
::..END nth-child..::*/
<html title='ROOT-HTML'>

<head title='L0-HEAD'>

</head>

<body title='L0-BODY [COMMON ANCESTOR]'>L0

  <div title='L1-DIV'>[XXXX] L1 [XXXX]</div>

  <span class="parent-span" title='L1-SPAN [POINT OF ENTRY]'>L1
    <div class="cta-wrapper" title='L2-DIV [TARGET ELEMENT]'>L2
        <div title='L3-DIV'>L3 [1]</div>
    </div>
</span>

  <span class="parent-span" title='L1-SPAN [POINT OF ENTRY]'>L1
    <div class="cta-wrapper" title='L2-DIV [TARGET ELEMENT]'>L2
        <div title='L3-DIV'>L3 [2]</div>
    </div>
</span>

  <span class="parent-span" title='L1-SPAN [POINT OF ENTRY]'>L1
    <div class="cta-wrapper" title='L2-DIV [TARGET ELEMENT]'>L2
        <div title='L3-DIV'>L3 [3]</div>
    </div>
</span>

  <div title='L1-DIV'>[XXXX] L1 [XXXX]</div>

</body>

</html>

The terms: common ancestor™, point of entry™, role™, and hierarchy™ are not standard terms, they are of my own creation because these rules are never explained very well AFAIK by anyone (myself included).

zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • thanks! very informative. yet I'm not sure I understand what is the role of classes within all the these? do classes ever help with targeting when we have "different levels", or do they help only once the desired level was already targeted, using the other rules? – rockyraw Feb 04 '18 at 10:30
  • People just overcomplicate how the :nth-* pseudos work. It really doesn't get any simpler than ":nth-child() matches the element when it's the nth child of its parent for the given n", e.g. I'm the third and youngest child in my family, which makes me :nth-child(3):last-child. That said, what do you think of my explanation [here](https://stackoverflow.com/questions/24657555/what-is-the-difference-between-first-child-and-first-of-type) of :first-child vs :first-of-type? – BoltClock Feb 04 '18 at 10:57
  • @rockyraw: The classes are there to give you an idea of what *purpose* these elements serve. You don't need to use them for hierarchical selection, but on the other hand, many style guides actually recommend creating classes specifically to make elements easier to target. You end up with a lot of what are called utility classes, which are either good or bad depending on your requirements, and your personal preferences. – BoltClock Feb 04 '18 at 10:59
  • @rockyraw Good question, I left the classes out to emphasize the predominate role `tagName`s and hierarchy have in determining the target. In general if you go the path of the `nth` then `#id`, `.classes`, `[attribute]`s are secondary. The reason why I included this ***common ancestor™*** idea is to convey the idea that it can be repeated if you happen to have several levels that split off to many siblings. As long as you have a common point at every split where siblings come from it's easier to think that way because `.class`es, `#id`s can get confusing when using `nth`. – zer00ne Feb 04 '18 at 13:53
1

Use last-of-type and first-of-type instead of last-child and first-child

The last-child / first-child address and count ALL siblings of the parent element: divs, spans, p etc.

The last-of-type / first-of-type selectors address only the one TYPE (the tag, not the class), i.e. the divs OR the spans OR the p tags etc.

.parent-span .cta-wrapper {
  background: gray;
}

.parent-span:last-of-type>.cta-wrapper {
  background: red;
}

.parent-span:first-of-type>.cta-wrapper {
  background: green
}
<div>xxxxxxxxx</div>

<span class="parent-span">
    <div class="cta-wrapper">
        <div>1</div>
    </div>
</span>


<span class="parent-span">
    <div class="cta-wrapper">
        <div>2</div>
    </div>
</span>


<span class="parent-span">
    <div class="cta-wrapper">
        <div>3</div>
    </div>
</span>


<div>xxxxxxxxx</div>
Johannes
  • 64,305
  • 18
  • 73
  • 130
  • Johannes I've read your explanation about `first-child` and `last-child` but I still don't understand why it doesn't work. When I test PO code with this simple css `span:first-child {background-color:green;}` this selector doesn't work either. – Masoud Keshavarz Feb 04 '18 at 09:06
  • hm... it seems a div inside span don't inherit css from its parent, even `span {background-color:green;}` don't work with PO code. – Masoud Keshavarz Feb 04 '18 at 09:09
0

In order for first-child and last-child pseudos to work your html content needs to be inside a list or some other series of items. Your spans are singular and so there's no first and last item.

See the unordered list I created and the styling to select first and last list item there.

In order for you to do this, put your spans in a div with the parent class, then select that parent div and then a first-child/last-child for a repeating element inside.

.parent-span .cta-wrapper {background:gray;}
.parent-span:last-child > .cta-wrapper {background:red;   } 
.parent-span:first-child > .cta-wrapper {background:green}

ul {
list-style: none;
margin: 2em 0 0 0;
padding: 0;
}

ul.parent-span li:first-child 
{
background:green;
}

ul.parent-span li:last-child 
{
background:red;
}
<div>xxxxxxxxx</div>

<span class="parent-span">
    <div class="cta-wrapper">
        <div>1</div>
    </div>
</span>


<span class="parent-span">
    <div class="cta-wrapper">
        <div>2</div>
    </div>
</span>      


<span class="parent-span">
    <div class="cta-wrapper">
        <div>3</div>
    </div>
</span>

<ul class="parent-span">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>


<div>xxxxxxxxx</div>
Nathaniel Flick
  • 2,902
  • 2
  • 22
  • 31