There’s been a lot written about CSS animations and transitions vs JavaScript or jQuery ones. Some people claim CSS is faster and smoother, and some claim JavaScript libraries like or GASP provide better performance.

In real life design I like to think of both as live options, where one is sometimes better than the other.

So instead of jumping into the debate, I thought I’d give an example of one instance in which I chose a CSS solution over a jQuery one. I’ll talk a little bit about the problems we were having, why I chose to go with a CSS solution, and how I implemented it.

The Original jQuery Animation

The owner of a project I’ve been working on wanted a hover effect on two of our horizontal menus. The effect was supposed to reveal each menu like a drawer on hover, and hide it again when hover ended.  One menu was fixed to the top of the screen, and the other was fixed to the bottom

When the product owner/developer starting running into weird behavior I said I’d take a look.

Problem #1 – Open/Close triggering multiple times

See the Pen Initial Open/Close Attempt by Kat Landreth (@katlandreth) on CodePen.

The developer’s first try at making this effect work used onmouseover() and onmouseout() on a wrapper around the list to trigger the hover effect , and toggle() to actually open and close the menus.

This didn’t work in a useable way because the effect was triggering again when descendant elements were hovered. That meant the menu was opening and closing multiple times every time you went to use it. Run your mouse over the hover areas in the Codepen above to see what I mean.

Sticking with jQuery, my first step was to use mouseenter() and mouseleave() instead of mouseover() and mouseout(). These do not get re-fired when children are hovered, which means they don’t make the menu open and close over and over.

Problem #2 – toggle() isn’t the desired effect

See the Pen Using mouseenter() mouseleave() by Kat Landreth (@katlandreth) on CodePen.

Fixing problem number one was easy enough. But, using toggle() to animate the effect wasn’t opening and closing the drawer the way we wanted.

Instead of sliding up and down, it was changing the width and height of the list. That’s what toggle() is supposed to do so the code was actually working as it should, but it’s not the drawer-like effect we were after.

So, I tried using slideToggle() in place of toggle().

This almost worked. In fact, it did work the way I expected on the bottom menu, but the top menu was revealing and hiding in a strange way. Instead of shifting the list itself up and down, it was like a curtain was being raised and lowered over the list to reveal and hide it (you can see this in the codepen below).

See the Pen Using slideToggle() by Kat Landreth (@katlandreth) on CodePen.

There was one additional problem. Notice, if you move your mouse over the trigger area and out of the pen, the entire open/close effect will still happen. I knew this would become annoying quickly so I had to come up with an approach that wouldn’t trigger the entire animation when the trigger was hovered quickly.

In the end, I decided to go with a pure CSS approach. I knew I could get the exact effect I was after without having to add a lot of extra code.

Pure CSS Menu Drawer Open and Close

Here’s the final CSS only menu.

See the Pen Pure CSS Open Close Menus on Hover by Kat Landreth (@katlandreth) on CodePen.

If you’re not really familiar with CSS transitions and hover effects, I’ll break down what’s going on.

Instead of using hide() to place the lists off screen, I did it in CSS by giving the top list a fixed position, and placing off screen 100px from the top or top:-100px;. I did the same for the bottom menu, but placed it 100px off screen from the bottom with bottom:-100px;.

The toplist and bottomlist each get a transition property like this transition:top 1s 1s ease;.

That transition tells the browser what property should be transitioned when it’s changed (top), how long the transition should take (1 second), how long the delay -if any- should be (1 second), and in what way the transition should speed up and/or slow down so it looks more realistic (ease).

Then comes the magic selector that selects the list when the trigger is hovered and the new position of the menu:

.toptrigger:hover .toplist{
  transition:top 1s ease;

When the trigger is hovered, that selector activates. The top position of the list changes from -100px to 0 (or 0px from the top of the viewport).

If you don’t set a transition property here, the one that’s set on the original list will apply. But, I wanted to have slightly different transitions for hover-on and hover-off so I used different transition rules for the hover and non hover states.

This rule basically says when the given property is active, transition from the old position to this one over the course of 1 second, and use easing to make the effect look more natural. There’s no delay, so the transition starts immediately when you hover the trigger.

The transition property that’s directly on toplist and bottom list says when the top or bottom property changes back to -100px (when hover ends) the position change should happen over the course of 1 second, there should be a 1 second delay between the property changing and the transition starting, and easing should be applied.

Both drawers open and close as expected, the effect is only fired once on hover of the trigger, and if you mouse on and off of the trigger quickly, the entire open/close doesn’t execute.

I’m not sure where this method would fall in the great CSS vs Javascript debate. I’m not really concerned about that though. I am interested in using the tools that will give me the correct outcome without compromising the quality of code or user experience. In the end, a few lines of CSS gave us exactly the effect that we wanted so I’d say that’s a win.

JQuery vs CSS Animations – A Real World Example