19

Short description (tl;dr;)

Is there a "pure CSS" way (not Less/Sass) of using the value of the nth-child inside the CSS properties? Something like this:

span.anim:nth-child(N) { animation-delay: N * 0.5s; }

If there is, how can I do it? If there is not, how could I mimic it in a clean way? (I'm sure that I'm over-complicating things here)


Long description

I was creating a simple animation in which letters fade in while rotating. For that, I was using a combination of javascript (with jQuery) and CSS, but that created some problems (see below); so I tried to move to a CSS-only solution, and that ended in a large amount of rules.

This (simplified) CSS is common to both solutions:

span.anim {
    font-size:30px;
    opacity:0;
    animation: anim 1s;
    animation-fill-mode: forwards;
}

@keyframes anim {
    from {
        transform: rotate(0deg);
        opacity:0;
    }
    to {
        transform: rotate(360deg);
        opacity:1;
    }
}

Using JavaScript + CSS

My JavaScript code looks like this:

var aux = $("#animate").text();
$("#animate").text("");

for (var x = 0; x < aux.length; x++) {
    setTimeout('$("#animate").append("<span class=\'anim\'>' + aux[x] + '</span>")', 500 * x);
}

This works great... unless the user moves to a different tab, then the animation messes up, and the result is unexpected. You can see it on this JSFiddle: http://jsfiddle.net/e7b9ygk4/ (run the script, move to a different tab, and go back after a few seconds).

Using only CSS

I can achieve the same effect by breaking the element in smaller elements (this is the part I feel I'm over-complicating), and then applying the animation to each element with a different delay:

span.anim:nth-child(1) { -webkit-animation-delay:0.5s; }
span.anim:nth-child(2) { -webkit-animation-delay:1.0s; }
span.anim:nth-child(3) { -webkit-animation-delay:1.5s; }
span.anim:nth-child(4) { -webkit-animation-delay:2.0s; }
span.anim:nth-child(5) { -webkit-animation-delay:2.5s; }
span.anim:nth-child(6) { -webkit-animation-delay:3.0s; }
span.anim:nth-child(7) { -webkit-animation-delay:3.5s; }
span.anim:nth-child(8) { -webkit-animation-delay:4.0s; }
span.anim:nth-child(9) { -webkit-animation-delay:4.5s; }
span.anim:nth-child(10) { -webkit-animation-delay:5.0s; }
span.anim:nth-child(11) { -webkit-animation-delay:5.5s; }
span.anim:nth-child(12) { -webkit-animation-delay:6.0s; }
span.anim:nth-child(13) { -webkit-animation-delay:6.5s; }
span.anim:nth-child(14) { -webkit-animation-delay:7.0s; }
span.anim:nth-child(15) { -webkit-animation-delay:7.5s; }
span.anim:nth-child(16) { -webkit-animation-delay:8.0s; }

This works great, even if the user moves to a different tab, but it results in a lot of unnecessary code (both HTML and CSS, although it could be simplified using JavaScript). You can see it working on this JSFiddle: http://jsfiddle.net/f5my3sm1/.

My gut tells me that this could be achieved using Sass/Less, but I want to know if there is a simple "pure CSS" way of doing this, something that could be achieved (hopefully) with a single rule like:

span.anim:nth-child(N) { animation-delay: N * 0.5s; }
Alvaro Montoro
  • 28,081
  • 7
  • 57
  • 86
  • 3
    Simply...no, there isn't – Paulie_D Mar 11 '15 at 14:48
  • I feared so. So am I over-complicating this? or the complicated way is the only way in this case? – Alvaro Montoro Mar 11 '15 at 14:52
  • 4
    Sass/Less were made to reduce unnecessary written code. But it would result in the same css. Wihtout CSS-Frameworks or JS it is not possible. I would say using JS is the shortest and best maintainable solution. – oshell Mar 11 '15 at 14:52
  • As you say, this would be very usefull when the length of the lists you'll be creating may be greatly variable. I wonder if this feature would make it to the css4 spec.... – Vandervals Dec 15 '16 at 11:06

2 Answers2

14

CSS variables have almost universal support on all modern browsers and can achieve virtually the same desired effect as @GreatBlake's answer using attr plus more. Instead of defining data in a user-defined attribute, you can define it as a custom property in the style attribute and get the value of those custom properties using the var() function:

span.anim {
  display: inline-block;
  font-size: 30px;
  opacity: 0;
  animation: fade-in-and-rotate 1s;
  animation-fill-mode: forwards;
  animation-delay: calc(var(--n) * 0.5s);
}

@keyframes fade-in-and-rotate {
  from {
    transform: rotate(0deg);
    opacity: 0;
  }
  to {
    transform: rotate(360deg);
    opacity: 1;
  }
}
<span class="anim" style="--n: 0">zero</span>
<span class="anim" style="--n: 1">one</span>
<span class="anim" style="--n: 2">two</span>
<span class="anim" style="--n: 3">three</span>
<span class="anim" style="--n: 4">four</span>

This code snippet also sets span.anim's display property to inline-block for the question's provided rotate animation to work.

Abdull
  • 26,371
  • 26
  • 130
  • 172
feihcsim
  • 1,444
  • 2
  • 17
  • 33
4

There is a very experimental CSS function for using attributes as properties for an element. Currently it only works on pseudo elements, so you'd have to consider coming up with some kind of clever re-working of your markup in this use case.

You could put the offset in a data-attribute on your markup (or with JS), and then use something like the following:

/* attr(name, units, fallback) */
span.anim {animation-delay: attr(offset, 1) * 0.5s;}
<span class="anim" offset="0"></span>
<span class="anim" offset="1"></span>
<span class="anim" offset="2"></span>
<span class="anim" offset="3"></span>
<span class="anim" offset="4"></span>

Please keep in mind that this is still a ways off from being widely accepted, and like I mentioned before, the above syntax wouldn't work currently because it would need to use pseudo elements like :after.

At the very least, it's something fun to think about.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
GreatBlakes
  • 3,971
  • 4
  • 20
  • 28
  • 1
    Not only does it only work with pseudo-elements, but it only works with the content property. Also see http://stackoverflow.com/questions/8769786/css3s-attr-doesnt-work-in-major-browsers/8769922#8769922 for more information on the attr() function. – BoltClock Apr 24 '15 at 16:46
  • Thanks! A very nice answer to my similar question: http://stackoverflow.com/questions/40761790/how-to-add-a-progressive-animation-delay-based-on-n-th-position?noredirect=1#comment68749717_40761790 – John Trichereau Nov 23 '16 at 13:26
  • 1
    Regarding ``: `offset` is not a valid attribute for `span` elements. It's better to use *data attributes* to make this valid HTML5: ``, then `attr(data-offset, 1)`. – Abdull Dec 08 '21 at 16:16