Contenu principal
CSS3 animation on click

CSS3 animation on click without JS

Today I share with you a little trick to trigger a CSS3 animation on click without javascript, with only pure CSS.
For this snippet, I’ll use a download link that will trigger an animated down arrow on click.

Let’s begin with the link with which we’ll work:

1

<a class="file_pdf" href="#">Download this pdf file</a>
1234567

.file_pdf {
	position: relative;
	display: inline-block;
	padding: 24px 0 0 50px;
	min-height: 26px;
	background: url(pdf-icon.png) -17px 0 no-repeat;
}

And our tiny arrow will be like this: (first we only place it in its initial position)

08091011121314151617

.file_pdf:before {
	content: '';
	display: block;
	width: 13px;
	height: 14px;
	position: absolute;
	left: 28px;
	top: -6px;
	background: transparent url(pdf-icon.png) 0 0 no-repeat;
}

Important note: Geoffrey pointed in comments that animations/transitions don’t work yet on pseudo-elements with Webkit. Neither with Opera by the way. So the only browser where these animations/transitions work is Firefox :( (perhaps with IE10?)

Now we can go further with the animation.

In CSS, unlike with javascript, we don’t have events, we only have selectors and pseudo-classes (like :hover or :focus). For the onclick event, we have something quite similar in css, it’s :active. This « state » is active when we click on a link. Great! Let’s try this!
For the beginning we’ll only focus on the movement, we’ll hide it later:

18192021222324

.file_pdf:active:before {
	animation: goDown .4s ease-in-out;
}
@keyframes goDown {
	from { top: -6px; }
	to   { top: 36px; }
}

Oh, wait! Nothing happen when I click!
Hmmm, yep, I think we need to learn more about :active to figure out what’s going on.

css-active-01 The :active « state » begins when we click the mouse button and ends when we release it. So it is the same for our animation: if we release the button before the animation ends, we won’t see it.


With javascript, the click event is triggered when we release the mouse button.

OK, in CSS, what appends at this moment?
The link goes from active to its normal state.
OK, now how can we « select » this « moment » with only CSS?
Precisely, we can’t, but we have a selector which can really help : :not(). With this, we can build a selector that will select the link when it is NOT active, so the animation will be launched when the link goes from its active state to the normal state.
Let’s try this:

18192021222324

.file_pdf:not(:active):before {
	animation: goDown .4s ease-in-out;
}
@keyframes goDown {
	from { top: -6px; }
	to   { top: 36px; }
}

Yata! It works! But… wait! The animation is also launched after page load now!
Yep, it’s the downside of this method, BUT, we can manage it by cheating ;)

But first things first, it’s the good time to hide the arrow at the beginning and the end of the animation.
Since we can’t use display: none; without destroying the animation, we’ll use opacity. We’ll add opacity: 0; to the arrow and deal with it into the animation:

08091011121314151617181920212223242526

.file_pdf:before {
	content: '';
	display: block;
	width: 13px;
	height: 14px;
	position: absolute;
	left: 28px;
	top: -6px;
	opacity: 0;
	background: transparent url(pdf-icon.png) 0 0 no-repeat;
}
.file_pdf:not(:active):before {
	animation: goDown .4s ease-in-out;
}
@keyframes goDown {
	from { opacity: 1; top: -6px; }
	60%  { opacity: 1; }
	to   { opacity: 0; top: 36px; }
}

Good, now we can deal with our page load problem.
The first thing to think is what we will use to hide (again) the arrow. We can’t use the display property or opacity: display will break our animation and opacity will be overwritten by the animation. But we have other possibilities: we could use overflow: hidden; on the link and place the arrow outside the link, but the downside is that we’ll can’t animate the arrow outside the link, we won’t see it. I chose to use the width property: width: 0; will hide the arrow somehow.

I first tried to do that with animation, but everything we add to our « goDown » animation will be done every time, not only on page load. Add another animation? Nop, the new animation will overwrite the « goDown » one.
There’s something else we can use: transitions.

Now here’s the tricky part. Look what happens with a linear transition : (we begin with 0px and we set 13px for the active state)

css-active-02

Even worse with a normal click (we don’t hold the button):

css-active-03

We need something like that:

css-active-04

I think most of you probably know the ease-in, ease-in-out, linear,… values for transition-timing-function, but there are 2 other values: step-start and step-end.
step-start: the element will take the final value of the transition at the beginning of the transition. We won’t see any visible transition.
step-end: the element will take the final value of the transition at the end of the period prescribed by it. We see that a delay has occurred (kind of).

When we release the button, the arrow needs to have a width of 13px: we’ll use step-start for the active state.
Then, the arrow must keep this width during the animation: we’ll use step-end for the normal state… I feel the headache coming…
In short:

08091011121314151617181920

.file_pdf:before {
	...
	width: 0;
	...
}
.file_pdf:not(:active):before {
	animation: goDown .4s ease-in-out;
	transition: width .4s step-end;
}
.file_pdf:active:before {
	width: 13px;
	transition: width .4s step-start;
}

Guess what, we have all we need!
Last detail, we add the browsers prefixes:

0102030405060708091011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465

.file_pdf {
	position: relative;
	display: inline-block;
	padding: 24px 0 0 50px;
	min-height: 26px;
	background: url(pdf-icon.png) -17px 0 no-repeat;
}

.file_pdf:before {
	content: '';
	display: block;
	width: 0;
	height: 14px;
	position: absolute;
	left: 28px;
	top: -6px;
	opacity: 0;
	background: transparent url(pdf-icon.png) 0 0 no-repeat;
}
.file_pdf:not(:active):before {
	-webkit-animation: goDown .4s ease-in-out;
	   -moz-animation: goDown .4s ease-in-out;
	    -ms-animation: goDown .4s ease-in-out;
	     -o-animation: goDown .4s ease-in-out;
	        animation: goDown .4s ease-in-out;
	-webkit-transition: width .4s step-end;
	   -moz-transition: width .4s step-end;
	    -ms-transition: width .4s step-end;
	     -o-transition: width .4s step-end;
	        transition: width .4s step-end;
}
.file_pdf:active:before {
	width: 13px;
	-webkit-transition: width .4s step-start;
	   -moz-transition: width .4s step-start;
	    -ms-transition: width .4s step-start;
	     -o-transition: width .4s step-start;
	        transition: width .4s step-start;
}

@-webkit-keyframes goDown {
	from { opacity: 1; top: -6px; }
	60%  { opacity: 1; }
	to   { opacity: 0; top: 36px; }
}
@-moz-keyframes goDown {
	from { opacity: 1; top: -6px; }
	60%  { opacity: 1; }
	to   { opacity: 0; top: 36px; }
}
@-ms-keyframes goDown {
	from { opacity: 1; top: -6px; }
	60%  { opacity: 1; }
	to   { opacity: 0; top: 36px; }
}
@-o-keyframes goDown {
	from { opacity: 1; top: -6px; }
	60%  { opacity: 1; }
	to   { opacity: 0; top: 36px; }
}
@keyframes goDown {
	from { opacity: 1; top: -6px; }
	60%  { opacity: 1; }
	to   { opacity: 0; top: 36px; }
}

See ya!

PS1: By the way, happy new year! :)
PS2: I hope you’ll forgive me for my English. ;)