6

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!

emjay
  • 1,491
  • 5
  • 17
  • 35

3 Answers3

5

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>
Community
  • 1
  • 1
Shaggy
  • 6,696
  • 2
  • 25
  • 45
  • That looks nice, thanks. But one addition. If there are no elements before or after Elements with class alternate, the line wouldn't work. I think that would be the solution: http://codepen.io/emjay/pen/aJGjZw Maybe you could change your answer :) – emjay Mar 23 '17 at 10:27
  • Okay, another problem. Even / Odd is not working like expected. Because it targets all elements in .elementWrapper not only .alternate. Please see here: http://codepen.io/emjay/pen/GWdXjJ – emjay Mar 23 '17 at 10:50
  • See the caveat in my answer regarding that: "*assuming it's irrelevant whether the odd or even elements have the alternate `background`*". However, if you give me a few minutes, I believe I can come up with a solution so that the first `.alternate` element will always have the same `background`, regardless of the number of elements that precede it. – Shaggy Mar 23 '17 at 10:54
  • That would be great, you have all time you need. :) – emjay Mar 23 '17 at 10:56
  • I may have spoken to soon! I assume the number of `.alternate` elements will be variable? Is there a maximum number that there can ever be? – Shaggy Mar 23 '17 at 11:06
  • You're right, the number is variable, and there is no maximum number. It can only be one element but also there can be 20 elements. I think I have to use $('.alternate:even').addClass('even'); for even and odd. – emjay Mar 23 '17 at 11:17
  • Yes, unfortunately, under those conditions, JavaScript is going to be the best solution for ensuring the first `.alternate` element is always the same colour, no matter the number of elements that precedes it. I've updated my answer with the necessary JS. – Shaggy Mar 23 '17 at 11:39
  • @Shaggy Great answer . I have added another answer targeting a particular case, where it is possible to use only CSS for the zebra style – vals Mar 23 '17 at 14:58
2

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;
}
nashcheez
  • 5,067
  • 1
  • 27
  • 53
  • That looks nice, thanks. But one addition. If there are no elements before or after Elements with class alternate, the line wouldn't work. I think that would be the solution: http://codepen.io/emjay/pen/aJGjZw Maybe you could change your answer :) – emjay Mar 23 '17 at 10:27
  • Modified my answer. :) – nashcheez Mar 23 '17 at 10:34
  • How does this differ from my solution? – Shaggy Mar 23 '17 at 10:49
  • Okay, another problem. Even / Odd is not working like expected. Because it targets all elements in .elementWrapper not only .alternate. Please see here: http://codepen.io/emjay/pen/GWdXjJ – emjay Mar 23 '17 at 10:50
1

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>
vals
  • 61,425
  • 11
  • 89
  • 138
  • Nice :) That's the route I was going too but dismissed it as being overly complex. I suggest updating your answer to address the borders as well, for the sake of completeness. – Shaggy Mar 23 '17 at 15:01
  • By the way, you can simplify the selectors for the first rule to just `div.alternate`. – Shaggy Mar 23 '17 at 15:42
  • Thank you ! I have edited the snippet following your suggestion. I didn't thought my answer as being complete, rather as a complement to you answer, that covered well the borders issue – vals Mar 24 '17 at 11:54