Even odd CSS makes sense

by zemion
Posted in HTML, CSS, JS

How can you format CSS elements based on their position? Say, every second element should be aligned to the right, whereas the first and last element should behave differently? This post gives some insight on how to approach such a problem.

Imagine the following situation: You have a Javascript slider on a webside, let's say in a webshop. Take for instance the webshop of alnaVela, a vegan online store with a great range of products. (Disclaimer: I'm a co-owner of the shop) What I have done there is formatting every second element on the other side. It's all done in CSS, and when an element is added, the slider adjusts automatically.

First of all, here is the CSS I will explain in depth (.parent and .child being classes for a parent and the child elements being styled).

.parent .child:nth-of-type(odd),
.parent .child:first-child:nth-last-of-type(even) {
  // styling for the odd elements, so the second, fourth, ... visible slide
}

.parent .child:nth-of-type(even),
.parent .child:first-child:nth-last-of-type(odd),
.parent .child:last-child {
  // styling for the even element, so the first, third, ... visible slide
}

Sliders usually have one peculiarity that needs to be delt with. To provide a smoother experience switching from one slide to another, the first visible slide is copied to the last position and the last visible slide is copied to the first position. So, a slideshow of A-B-C becomes C-A-B-C-A. Lets make is even clearer by calling the original, visible slide Av, and the copy Ac. The slideshow then becomes Cc-Av-Bv-Cv-Ac. Now the transition can always be done by moving left and right (for instance from Cv to Ac, and after the transition is done, the pointer is shifted from Ac to Av, unnoticed by the user (they look exactely the same).

The task at hand was to develop a stylesheet that treats every second element differently, while also styling the copied elements (Cc and Ac) like there visible counterparts (Cc and Ac). CSS offers the tool to achieve that. To select every second child (that is, the even and the odd elements, you use the :nth-of-type(x) pseudo selector, where x is even or odd. This takes care of the visible elements. For the copy of Av the task is also quite easy - Ac is always in last position, the CSS pseudo selector :last-child takes care of that, and Av is always in an even position, so Ac should be styled the same way.

The tricky part is the styling of the first element Cc, which is a copy of the second to last element. Now, there is no way of knowing if the second to last element is an even or an odd element, as CSS is not capable of counting. What we can do, however, is a little bit of CSS sorcery. By chaining and thus combining the CSS pseudo selectors :first-child and :nth-last-of-type(x) (x again being even or odd), we can style the first element if and only if it is an even (or odd) element when counting from the back. A CSS selector of :first-child:nth-last-of-type(odd) will select the first element, if the second to last element is even. Wait, what?

Okay, maybe an example will help. Lets make a list, marking the even and odd elements:

  • Cc - odd
  • Av - even
  • Bv - odd
  • Cv - even
  • Ac - odd

As we can see, the second to last element is even, wheras the first element (even if seen backwards, as we clearly see the symmetry) is odd.

Now we expand the list to get an even number of elements (in brackets we put the parity as seen from the end of the list):

  • Dc - odd (even)
  • Av - even (odd)
  • Bv - odd (even)
  • Cv - even (odd)
  • Dv - odd (even)
  • Ac - even (odd)

Here, it is exactly the other way around, the second to last element Dv is odd, so the first element, as seen from the end of the list, is even.

When we combine all those into one nifty stylesheet, we get the result shown above. Final note: The order is important, as the :last-child pseudo selector has to override all other selectors.

Did you like this text? Maybe it even helped you? I certainly hope it did. If you want to show your appreciation, you can send me a tip.