DBS
DBS

Reputation: 9984

Select first-of-type class after using a not() selector

I'm attempting to select the first item from a group, however I want to exclude certain items from the selection by class.

e.g. In this example, I attempt to select the first .item that doesn't have the .hide class. The not() works, correctly selecting all but the first item, however following first-of-type doesn't seem to match anything.

To clarify, in this example I'm aiming for:

.item {
  background-color: #060;
  margin-bottom: 2px;
  text-align: center;
  color: #FFF;
}
.hide {
  background-color: #600;
}
/* The important bit */
.item:not(.hide):first-of-type {
  background-color: #006;
}
<div class="item hide">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item hide">5</div>
<div class="item">6</div>

Am I writing the selector incorrectly? Is there an alternative way to do this?

Upvotes: 2

Views: 991

Answers (2)

John Slegers
John Slegers

Reputation: 47091

The code

This code has the desired effect :

.item {
    background-color: #006;
    margin-bottom: 2px;
    text-align: center;
    color: #FFF;
}

.hide {
    background-color: #600;
}

.item:not(.hide) ~ .item:not(.hide) {
    background-color: #060;
}
<div class="item hide">1</div>
<div class="item hide">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item hide">5</div>
<div class="item">6</div>

(see also this Fiddle)

Upvotes: 0

BoltClock
BoltClock

Reputation: 723658

As all your items are divs, if they are the only children in their parent then :first-of-type is equivalent to :first-child. See this answer for more details.

Since you can't directly match the first child with a class (or any other sub-selector) you will need to do this another way, using an overriding rule.

The simplest way to do this is to:

  1. Make all items blue.
  2. Make .hide items red (as intended).
  3. Apply the green styles to .item:not(.hide) ~ .item:not(.hide). That is, all items that don't have the .hide class, that follow the first such item.

This accounts for all possible cases with just one assumption: that if the first element doesn't have the .hide class then it should be blue as well:

.item {
  background-color: #006;
  margin-bottom: 2px;
  text-align: center;
  color: #FFF;
}
.hide {
  background-color: #600;
}
.item:not(.hide) ~ .item:not(.hide) {
  background-color: #060;
}
section {
  float: left;
  width: 20%;
  margin: 20px;
}
<section>
    <div class="item hide">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item hide">5</div>
    <div class="item">6</div>
</section>
<section>
    <div class="item hide">1</div>
    <div class="item hide">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item hide">5</div>
    <div class="item">6</div>
</section>
<section>
    <div class="item">1</div>
    <div class="item hide">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item hide">5</div>
    <div class="item">6</div>
</section>

If the first element may lack the .hide class, but you only want the first .item:not(.hide) after a .hide item to be blue, then it becomes impossible without JavaScript.

Upvotes: 1

Related Questions