Update to my highlight/fadeout example - now with CSS hotness

This post is more than 2 years old.

Yesterday I posted an example of highlight/fadeout effects done with jQuery. It wasn't necessarily that exciting, but it's not something I've done before so it was fun to build. Fellow evangelist (and my boss, so yeah, his comments get special attention) Kevin Hoyt commented that what I had achieved would be possible with CSS and transitions.

Now - I will admit to being a bit codependent on jQuery. It's not like I'm addicted. I can stop using jQuery whenever I want to. In fact, I went out of my way to in this post. I love jQuery, but I can definitely recognize that I'm almost depending on it like a crutch, so I thought I'd take the opportunity to try it with pure CSS.

Before even approaching the idea of an animation, I thought I'd try a simple hover. That turned out to be trivial:

.tagcloudword { opacity: 0.5; }

.tagcloudword:hover { opacity:1; font-weight: bold; }

That worked well enough where even if everything else I did failed, I'd be ok. Notice I also added the bold there to make it a bit more fancy. You can try this here: http://www.raymondcamden.com/demos/2012/feb/3/take2.html

I knew next to nothing about animation and transitions in CSS, but quickly found the docs at MDN to be incredibly useful. Begin with their CSS page: https://developer.mozilla.org/en/CSS. They've got a page on animations and transitions. Obviously transitions are what we want here.

I won't try to replicate the excellent docs over at MDN, but the basic gist of it seems to be this:

  • Define your CSS for the item.
  • Define your CSS for the hover.
  • Then tell the CSS what properties are changing, how it changes (from a list of options like ease-in), and the duration.

The only thing missing from the docs that may trip you up is the use of vendor prefixes. MDN focuses on what works for them - the moz prefix. For Chrome, I had to add webkit as well. This means you have to duplicate your code, which is kinda sucky, but passable. So here was my first update:

.tagcloudword { opacity: 0.1; -webkit-transition: opacity; -webkit-transition-timing-function: ease-out; -webkit-transition-duration: 2000ms; -moz-transition: opacity; -moz-transition-timing-function: ease-out; -moz-transition-duration: 2000ms; }

.tagcloudword:hover {
    opacity: 1;
    -webkit-transition: opacity;
    -webkit-transition-timing-function: ease-out;
    -webkit-transition-duration: 2000ms;
    -moz-transition: opacity;
    -moz-transition-timing-function: ease-out;
    -moz-transition-duration: 2000ms;
}

That's a lot of code, but if you ignore the moz lines it's really just 3 lines per state. Something else not immediately obvious to me was that if you define the transitions on hover and not on the core style block, you won't get an animation backwards from your hover state. You can demo this here: http://www.raymondcamden.com/demos/2012/feb/3/test3.html Be sure to notice that I slowed the transition down quite a bit to make it more obvious. I'd probably use 500ms instead.

Ok - finally. So how about multiple changes? Just add additional properties. Here's how I added some color too. In Chrome it seems to work really well. It goes from black to blood red to a pure red. (Don't hate me for color choices.)

.tagcloudword { opacity: 0.1; color: black; -webkit-transition: opacity,color; -webkit-transition-timing-function: ease-out; -webkit-transition-duration: 2000ms; -moz-transition: opacity,color; -moz-transition-timing-function: ease-out; -moz-transition-duration: 2000ms; }

.tagcloudword:hover {
    opacity: 1;
    color: red;
    -webkit-transition: opacity,color;
    -webkit-transition-timing-function: ease-out;
    -webkit-transition-duration: 2000ms;
    -moz-transition: opacity,color;
    -moz-transition-timing-function: ease-out;
    -moz-transition-duration: 2000ms;
}

You can view this one here: http://www.raymondcamden.com/demos/2012/feb/3/test4.html

It works in Chrome and Firefox. In IE9 it is not supported, but it fails ok. You still get a hover effect.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Phillip Senn posted on 2/3/2012 at 9:01 PM

Ah, this is nice. And if the user has JavaScript turned off, the css method will continue to work.
http://caniuse.com/ is always mentioned when it comes to css3 techniques.
Oh, BTW, sorry about "going off" in your previous blog post. I've been giving it some thought over the past few days.

Comment 2 by Raymond Camden posted on 2/3/2012 at 9:14 PM

Don't apologize. I love it when topics mutate.

Comment 3 by Arthur Nweke posted on 2/3/2012 at 9:35 PM

Ray,
you posted this "<cfajaxproxy bind="cfc:test.checkArtist({artist@keyup})" onSuccess="handleConflict">
<script>
function handleConflict(r){
if (r == true) {
document.getElementById("warning").innerHTML="This artist exists.";
document.getElementById("submitBtn").setAttribute("disabled", "disabled");
}
else {
document.getElementById("warning").innerHTML="";
document.getElementById("submitBtn").removeAttribute("disabled");
}
}
</script>

<cfform name="mainForm">
New Artist: <cfinput name="artist"><br/>
<div id="warning"></div>
<input type="submit" id="submitBtn" value="Save">
</cfform>

remote function checkArtist(string n) {
var q = new com.adobe.coldfusion.query();
q.setDatasource("cfartgallery");
q.setSQL("select lastname from artists where upper(lastname) = :name");
q.addParam(name="name",value="#ucase(arguments.n)#",cfsqltype="cf_sql_varchar");
var res = q.execute().getResult();
return res.recordCount==1;
}
" is it posible to call the remote function with javascript so that you have the user stay in the field until they enter something that is not found in the database?

Comment 4 by Kevin Hoyt posted on 2/3/2012 at 9:40 PM

I'm so spoiled. Thanks, Ray.

Comment 5 by Seb Duggan posted on 2/3/2012 at 9:42 PM

Ray - to save yourself some typing, you only actually need to apply the transitions to .tagcloudword (not to the :hover state). This then controls the transitions for all states of that class...

Comment 6 by Raymond Camden posted on 2/3/2012 at 10:11 PM

Arthur - your comment doesn't make sense. Can you please email me directly.

Seb - mind sharing an example of that? (Use pastebin of course, or post it on your server.)

Comment 7 by Sam Farmer posted on 2/3/2012 at 11:14 PM

"This means you have to duplicate your code, which is kinda sucky, but passable."

This is one of the advantages of using less css. It wouldn't make a great difference in your code example but for the transition code you code write a mixin and call it twice. The mixin would then output the css with all the different browser prefixes.

It would look something like:

.transition() {
-webkit-transition: opacity,color;
-webkit-transition-timing-function: ease-out;
-webkit-transition-duration: 2000ms;
-moz-transition: opacity,color;
-moz-transition-timing-function: ease-out;
-moz-transition-duration: 2000ms;
}

(And it could also take in arguments if needed).

The class definitions would look like:

.tagcloudword {
opacity: 0.1;
color: black;
.transition();
}

.tagcloudword:hover {
opacity: 1;
color: red;
.transition();
}

Comment 8 by Raymond Camden posted on 2/4/2012 at 12:18 AM

I haven't really added Less to my developer stack, but I'm very impressed by it. I'm not a fan of CoffeeScript since JS seems simple/powerful enough, but CSS _definitely_ needs this.

Comment 9 by Richard posted on 2/4/2012 at 1:10 AM

Or just:

[code]
.tagcloudword {
opacity: 0.1;
-webkit-transition: all 2s ease-out;
-moz-transition: all 2s ease-out;
}

.tagcloudword:hover {
opacity: 1;
-webkit-transition: all 2s ease-out;
-moz-transition: all 2s ease-out;
}
[/code]

Comment 10 by Raymond Camden posted on 2/4/2012 at 1:14 AM

What is "all" a shorthand for? Does it mean "All the crap I defined in this style block" ?

Comment 11 by blackjk posted on 2/4/2012 at 1:49 AM

All means all properties specified in the rule should be transitioned. Your example for just opacity could be shortened to below.

.tagcloudword { opacity:0.5;
-webkit-transition: opacity 2s ease-out;
-moz-transition: opacity 2s ease-out;
transition: opacity 2s ease-out;
}
.tagcloudword:hover { opacity:1.0; }

What seb is eluding to above is the fact that you don't need to define the transition styles again on the hover pseudo selector.

Comment 12 by Raymond Camden posted on 2/4/2012 at 2:28 AM

Nope, I'm not seeing that. When I do this:

.tagcloudword {
opacity: 0.1;
color: black;
-webkit-transition: opacity,color 2s ease-out;
-moz-transition: opacity,color 2s ease-out;
}

.tagcloudword:hover {
opacity: 1;
color: red;
}

It correctly works _to_ the hover state, but when I mouse out, it instantly returns.

(So to be clear, I agree with you about the shorthand, and thank you, but don't agree, from what I see, that you can skip defining stuff on both 'sides')

Comment 13 by Raymond Camden posted on 2/4/2012 at 2:30 AM

Actually, something funky is going on. Check it here:

http://www.raymondcamden.co...

It seems to change the opacity immediately. Only color goes over time.

Comment 14 by Raymond Camden posted on 2/4/2012 at 2:31 AM

Ah, ok, this is where you use "all" instead of listing each item.

http://www.raymondcamden.co...

Works perfectly here, and a lot less typing.

Thanks Richard/Seb/Blackjk/all.