Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

target first, even/odd and last element with class, without wrapper, among other elements (nth-of-class behaviour) :)

Tags:

css

I have the following structure:

<div class="elementWrapper">
  <div>1. Element</div>
  <div>2. Element</div>
  <div>3. Element</div>
  <div class="alternate">1. Element with spec. Class</div>
  <div class="alternate">2. Element with spec. Class</div>
  <div class="alternate">3. Element with spec. Class</div>
  <div class="alternate">4. Element with spec. Class</div>
  <div class="alternate">5. Element with spec. Class</div>
  <div>9. Element</div>
  <div>10. Element</div>
  <div>11. Element</div>
</div>

There can be a unknown number of multiple elements before and after, and I have no possibility to add a "wrapping" div around the elements with the class="alternate". (where everything would be fine).

I would like to give the first .alternate Element a border top, the last .alternate Element a border bottom. And all .alternate elements should have a different background color for each row (even/odd).

I've tried it in different ways, and I know that nth-of-type and nth-child won't work, because there is no wrapping div around my .alternate elements, and so it can't work because all Elements are counted for even/odd and so on.

So I made a pen with the problem and possible Solutions:

http://codepen.io/emjay/pen/RpyyOo

I would like to ask you, what the best way could be -without changing the structure-. Is there a working css only solution?

Thanks for your help!

like image 749
emjay Avatar asked Mar 23 '17 09:03

emjay


People also ask

How do you find the nth element using CSS selector?

Definition and Usage. The :nth-child(n) selector matches every element that is the nth child of its parent. n can be a number, a keyword (odd or even), or a formula (like an + b). Tip: Look at the :nth-of-type() selector to select the element that is the nth child, of the same type (tag name), of its parent.

What is the nth child in CSS?

The :nth-child selector allows you to select one or more elements based on their source order, according to a formula. It is defined in the CSS Selectors Level 3 spec as a “structural pseudo-class”, meaning it is used to style content based on its relationship with parent and sibling elements.

How do you select first and second child in CSS?

The CSS child selector has two selectors separated by a > symbol. The first selector indicates the parent element. The second selector indicates the child element CSS will style.


3 Answers

For the first question (adding border-top to the first .alternate element and border-bottom to the last .alternate element), you can achieve this by adding border-top alone to:

  • the first .alternate element that immediately follows an element that does :not() have the .alternate class, and,
  • also to the first element that does not have the .alternate class that immediately follows an element that does.

In the case that there are no elements before the first .alternate element, you will also need to add border-top to the .alternate element that is also the :first-child and, in the case that there are no elements after the last .alternate, you will need to add border-bottom to the .alternate element that is also the :last-child.

For the second question on "zebra striping" the .alternate elements, assuming it's irrelevant whether the odd or even elements have the alternate background, you can achieve that with a simple :nth-of-type() (or :nth-child()) pseudo-class. However, if you require the first .alternate element to always have the same background regardless of the number of elements that precede it, you will need to resort to JavaScript - it is possible with CSS alone but requires a ridiculous number of selectors (see this answer as an example).

(function(){
  var wrappers=document.querySelectorAll(".elementWrapper"),
      x=wrappers.length,
      divs,y,alt;
  while(x--){
    divs=wrappers[x].querySelectorAll(".alternate");
    y=divs.length;
    alt=!(y%2);
    while(y--)
      divs[y].classList.add((alt=!alt)?"odd":"even");
  }
})();
/** JQuery **/
//$('.alternate:odd').addClass('odd')
//$('.alternate:even').addClass('even');
.elementWrapper>div:not(.alternate)+div.alternate,
.elementWrapper>div.alternate+div:not(.alternate),
.elementWrapper>div.alternate:first-child{
  border-top:1px solid #000;
}
.elementWrapper>div.alternate:last-child{
  border-bottom:1px solid #000;
}
.elementWrapper>div.alternate.odd{
  background:#ccc;
}
.elementWrapper>div.alternate.even{
  background:#eee;
}

/** Uncomment below for CSS-only zebra-striping **/
/*.elementWrapper>div.alternate:nth-of-type(odd){
  background:#ccc;
}
.elementWrapper>div.alternate:nth-of-type(even){
  background:#eee;
}*/

/** "housekeeping" **/.elementWrapper{background:#fff;color:#000;margin:0 0 20px;}.elementWrapper>div{font-family:sans-serif;font-size:14px;overflow:hidden;padding:5px;text-overflow:ellipsis;white-space:nowrap;}
<div class="elementWrapper">
  <div>1. Element</div>
  <div class="alternate">1. Element with spec. Class</div>
  <div class="alternate">2. Element with spec. Class</div>
  <div class="alternate">3. Element with spec. Class</div>
  <div class="alternate">4. Element with spec. Class</div>
  <div class="alternate">5. Element with spec. Class</div>
  <div>9. Element</div>
</div>
<div class="elementWrapper">
  <div>1. Element</div>
  <div>2. Element</div>
  <div class="alternate">1. Element with spec. Class</div>
  <div class="alternate">2. Element with spec. Class</div>
  <div class="alternate">3. Element with spec. Class</div>
  <div class="alternate">4. Element with spec. Class</div>
</div>
like image 122
Shaggy Avatar answered Sep 22 '22 23:09

Shaggy


Building on @Shaggy's answer, using :not & nth-of-type selectors, you can render what is required in pure css.

Refer code:

.elementWrapper div:not(.alternate)+div.alternate,
.elementWrapper div.alternate+div:not(.alternate) {
  border-top: 2px solid blue;
}

.elementWrapper div.alternate:nth-of-type(odd) {
  background: green;
}

.elementWrapper div.alternate:nth-of-type(even) {
  background: red;
}
<div class="elementWrapper">
  <div>1. Element</div>
  <div>2. Element</div>
  <div>3. Element</div>
  <div class="alternate">1. Element with spec. Class</div>
  <div class="alternate">2. Element with spec. Class</div>
  <div class="alternate">3. Element with spec. Class</div>
  <div class="alternate">4. Element with spec. Class</div>
  <div class="alternate">5. Element with spec. Class</div>
  <div>9. Element</div>
  <div>10. Element</div>
  <div>11. Element</div>
</div>

As pointed out by the OP, if there are no div prior or after .alternate elements, the above solution won't work. The usage of first-child and last-child would be preferred then.

/* if there are no elements before .alternate */
.elementWrapper > div.alternate:first-child{
  border-top: 2px solid blue;
}

/* if there are no elements after .alternate */
.elementWrapper > div.alternate:last-child{
  border-bottom: 2px solid blue;
}
like image 20
nashcheez Avatar answered Sep 24 '22 23:09

nashcheez


If your .alternate elements concentrate in one region (that is , they are all together) you can get the zebra styling with these styles:

.alternate {
  background: gray;
}

div:nth-of-type(odd):not(.alternate)+div.alternate~div.alternate:nth-of-type(even),
div:nth-of-type(odd):not(.alternate)+.alternate:nth-of-type(even),
div:nth-of-type(even):not(.alternate)+div.alternate~div.alternate:nth-of-type(odd),
div:nth-of-type(even):not(.alternate)+.alternate:nth-of-type(odd) {
  background: red;
}
<div class="wrap">
  <div class="col">
    <div class="elementWrapper">
      <div>1. Element</div>
      <div>2. Element</div>
      <div>3. Element</div>
      <div class="alternate">1. Element with spec. Class</div>
      <div class="alternate">2. Element with spec. Class</div>
      <div class="alternate">3. Element with spec. Class</div>
      <div class="alternate">4. Element with spec. Class</div>
      <div class="alternate">5. Element with spec. Class</div>
      <div>9. Element</div>
      <div>10. Element</div>
      <div>11. Element</div>
    </div>
  </div>
  <div class="col">
    <div class="elementWrapper">
      <div>1. Element</div>
      <div>2. Element</div>
      <div>3. Element</div>
      <div>4. Element</div>
      <div class="alternate">1. Element with spec. Class</div>
      <div class="alternate">2. Element with spec. Class</div>
      <div class="alternate">3. Element with spec. Class</div>
      <div class="alternate">4. Element with spec. Class</div>
      <div class="alternate">5. Element with spec. Class</div>
      <div>10. Element</div>
      <div>11. Element</div>
      <div>12. Element</div>
    </div>
  </div>
</div>
like image 23
vals Avatar answered Sep 23 '22 23:09

vals