Interplay between CSS and Javascript

Animating the height of an element with only CSS is possible with the max-height property, giving the begin value a max-height of zero and (let's say) the 'hovered' value some high number “that will always be higher then the real height of the highest animated element”. The outcome of the results can vary quite a bit, depending on the differences between all these values: not satisfying in all scenarios. An alternative way is using height: 0 in combination with a set value for padding-bottom.

Animating padding-bottom with CSS-Transitions

But to know these values for padding-bottom 1 one will have to know the height of each element. With only a small amount of dropdown-lists to be styled, one might be tempted to hardcode those values in. But what if someone (, and maybe even not you) wants to add an item to one of these lists in the future? Now one will have to change this value in the CSS every time again when the number of items in the HTML changes, which can be anoying.

Javascript to the rescue

Since one can know the height of one individual list-item in the dropdown, (because we have a set font-size, padding, and even use a web-font), one can calculate the height of each list by multiplying this value with the number of list-items that are in the list. To count the number of list-items in each dropdown list with javascript one can use the length method.

var itms = {
home    : ($("#tt-home li").length),
blog    : ($("#tt-blog li").length),
books   : ($("#tt-books li").length),
goodies : ($("#tt-goodies li").length),
media   : ($("#tt-media li").length),
news    : ($("#tt-news li").length),
contact : ($("#tt-contact li").length)
};

Now one can add the outcome: this number, on a class, which is then added with javascript on each parent dropdown list-item. In jQuery we can use $each in a function to get the number of list-items in each list:

$.each( itms, function( i, val ) {
  $( "#tt-" + i ).addClass('tt-nav__itemcount-'+val);
});

Switching to Sass, we can make a function there which will output the values for padding-bottom on every matching “submenu-parent” like so:

$item-count-list: 1 2 3 4 5 6 7 8 9 10;
$item-value-list: 46px 92px 138px 184px 230px 276px 322px 368px 414px 460px;
@for $i from 1 through length($item-count-list) {
  [class*="itemcount-#{$i}"]:hover .tt-nav__submenu {
    padding-bottom: nth($item-value-list, $i);
   }
}

The generated CSS 2:

[class*="itemcount-5"]:hover .tt-nav__submenu {
  padding-bottom: 230px;
}

Minimal transition in CSS

In the CSS one should have declarations for the different ‘states’, but the definition can be quite minimal:

/* Submenu - shared properties and values */
.tt-nav__submenu {
  transition: padding-bottom .75s cubic-bezier(0.6, 0.4, 0.25, 0.75);
  height: 0;
  padding: 0;
}

.tt-nav__item:hover .tt-nav__submenu {
// maybe include a fallback for padding-bottom when javascript is not available:
  padding-bottom: 460px;
}

// Sass function after this,
// outputting the different values for `padding-bottom`
// as explained above:
// $item-count-list: 1 2 3 4 5 6 7 8 9 10;
// $item-value-list: 46px 92px ... and so on;

One can also provide a fall-back value for padding-bottom on the active/hover state (value should be matching the highest current element, which will then be overridden by the values outputted by the Sass-function) in order to make it always appear, even without javascript (, as shown above).

Interplay

Then there it is: a DIY 3 cooperation between CSS and Javascript, responding fast and snappy, because it benefits from modern browser’s native capabilities with using CSS-transitions.

Examples

  • responsive dropdown demo - Disclaimer: this is a prototype: there will be more robust javascript implementations for dropdown menu’s tested on multiple devices out there (like Foundation/Bootstrap).
  • example build on Bootstrap-3 - extended with hover-dropdown-plugin by Cameron Spear - fork of repo on Github - toggle links are clickable, as far as I know works on mobile, but javascript could be improved upon (I am aware that the animation could be done with jQuery slideDown() too, I just like the DIY 3 aspect: the tinkering, and the fact the transitions are done by the CSS, which has it's own (long-term) advantage.)

Notes

  1. Probably can also use height instead of padding-bottom since we are not using height: auto here, which (used to?) require max-height instead of height, but I wanted to be carefull here, avoid confusion by not mixing those concepts.
  2. One of them declarations: it outputs 10 of those, see also this Github Gist, or this reduced testcase here on CodePen
  3. do it yourself

This article was also published on Codepen.