Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use the nth-child value as a parameter in a property? (and how)

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; }
like image 902
Alvaro Montoro Avatar asked Mar 11 '15 14:03

Alvaro Montoro


2 Answers

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.

like image 123
feihcsim Avatar answered Oct 03 '22 00:10

feihcsim


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.

like image 21
GreatBlakes Avatar answered Oct 03 '22 00:10

GreatBlakes