Freebie: Bootstrap DropDown Effects with Animate.css

Here’s the first in a long list of tutorials / code snippets to come here on Bootbites.com, This one is aimed at spicing up your Bootstrap dropdowns with some cool effects from Animate.css.

We’re going to take a bog standard Bootstrap navbar with dropdowns and then hook into the Bootstrap Dropdown Javascript Events/API to trigger the animation effects provided by Animate.css on dropdown open & close.

First Things First

I’m assuming you have the following in place OR have downloaded the demo files:

  • Animate.css downloaded or using a CDN (https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.4.0/animate.min.css) & included in your page
  • Bootstrap downloaded or using the CDN (https://www.bootstrapcdn.com/) & included in your page, both CSS & JS files
  • A basic HTML page with no content

HTML

The Basics

The HTML is a simple Bootstrap navbar with dropdowns much like the examples you’ll find in the Bootstrap documentation.

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <ul class="nav navbar-nav">
      <!-- dropdown 1 -->
      <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
          fadeIn / fadeIn
          <span class="caret"></span>
        </a>
        <ul class="dropdown-menu">
          <li><a href="#">Action</a></li>
          <li><a href="#">Another action</a></li>
          <li><a href="#">Something else here</a></li>
        </ul>
      </li>
      
      <!-- dropdown 2 -->
      <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
          bounceIn / bounceOut
          <span class="caret"></span>
        </a>
        <ul class="dropdown-menu">
          <li><a href="#">Action</a></li>
          <li><a href="#">Another action</a></li>
          <li><a href="#">Something else here</a></li>
        </ul>
      </li>
    </ul>
  </div>
</nav>

Some data Attribute Magic

data what?

Bootstrap loves them, we love them, everyone the coding world loves HTML5 “data” attributes. For those new to data attributes, these are valid, flexible attributes you can apply to your HTML tags which allow you to embed “data” into your page in a clean & valid manner. From there this data can be read by Javascript (jQuery provides a “data()” function for this) to provide settings or other page enhancements. You can call them what you like (data-chicken-and-chips for example) as long as it’s all lowercase and the values passed must be a string, you can even pass values in a JSON syntax.

In Bootstrap you’ll see data-toggle="COMPONENT" data attributes used as a way to trigger Javascript components and sometimes data-target="VALUE" to specific which element should be the target for a given component. For example, dropdown functionality can be triggered using a data attribute on a link or button, like so:

<button type="button" data-toggle="dropdown">Dropdown trigger</button>

Find out more about data attributes.

2 Custom data Attributes

Back to our example and we’re going to add 2 simple data attributes to our .dropdown-menu elements to tell jQuery which animation effects should be applied on open & close of the given dropdown menu. These go in the form of:

  • data-dropdown-in – specifies the animation by name to be applied when the dropdown menu is opened
  • data-dropdown-out – specifies the animation by name to be applied when the dropdown menu is closed

You can specify any Animate.css effect by name as the value, although you should try to use “in” animations (ie. fadeIn) for open/in effects and “out” animations (ie. fadeOut) for close/out effects otherwise things might look a little strange.

We’ll add a fadeIn and fadeOut effect to our first .dropdown-menu element like so:

<ul class="dropdown-menu" data-dropdown-in="fadeIn" data-dropdown-out="fadeOut">
......
</ul>

Javascript

Right now our HTML code does nothing so it’s time to write a small chunk of jQuery to hook into the Bootstrap Dropdown Javascript Events/API so our animation effects are triggered on open and close of our dropdown menus.

var dropdownSelectors = $('.dropdown, .dropup');

// Custom function to read dropdown data
// =========================
function dropdownEffectData(target) {
  // @todo - page level global?
  var effectInDefault = null,
      effectOutDefault = null;
  var dropdown = $(target),
      dropdownMenu = $('.dropdown-menu', target);
  var parentUl = dropdown.parents('ul.nav'); 

  // If parent is ul.nav allow global effect settings
  if (parentUl.size() > 0) {
    effectInDefault = parentUl.data('dropdown-in') || null;
    effectOutDefault = parentUl.data('dropdown-out') || null;
  }
  
  return {
    target:       target,
    dropdown:     dropdown,
    dropdownMenu: dropdownMenu,
    effectIn:     dropdownMenu.data('dropdown-in') || effectInDefault,
    effectOut:    dropdownMenu.data('dropdown-out') || effectOutDefault,  
  };
}

// Custom function to start effect (in or out)
// =========================
function dropdownEffectStart(data, effectToStart) {
  if (effectToStart) {
    data.dropdown.addClass('dropdown-animating');
    data.dropdownMenu.addClass('animated');
    data.dropdownMenu.addClass(effectToStart);    
  }
}

// Custom function to read when animation is over
// =========================
function dropdownEffectEnd(data, callbackFunc) {
  var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
  data.dropdown.one(animationEnd, function() {
    data.dropdown.removeClass('dropdown-animating');
    data.dropdownMenu.removeClass('animated');
    data.dropdownMenu.removeClass(data.effectIn);
    data.dropdownMenu.removeClass(data.effectOut);
    
    // Custom callback option, used to remove open class in out effect
    if(typeof callbackFunc == 'function'){
      callbackFunc();
    }
  });
}

// Bootstrap API hooks
// =========================
dropdownSelectors.on({
  "show.bs.dropdown": function () {
    // On show, start in effect
    var dropdown = dropdownEffectData(this);
    dropdownEffectStart(dropdown, dropdown.effectIn);
  },
  "shown.bs.dropdown": function () {
    // On shown, remove in effect once complete
    var dropdown = dropdownEffectData(this);
    if (dropdown.effectIn && dropdown.effectOut) {
      dropdownEffectEnd(dropdown, function() {}); 
    }
  },  
  "hide.bs.dropdown":  function(e) {
    // On hide, start out effect
    var dropdown = dropdownEffectData(this);
    if (dropdown.effectOut) {
      e.preventDefault();   
      dropdownEffectStart(dropdown, dropdown.effectOut);   
      dropdownEffectEnd(dropdown, function() {
        dropdown.dropdown.removeClass('open');
      }); 
    }    
  }, 
});

 

Javascript Explained

  • dropdownSelectors variable: Is used to select to “dropdown” trigger elements, you shouldn’t need to alter this unless you have some custom dropdown code.
  • dropdownEffectData function: This function reads the “data” attributes from the target ul.dropdown-menu element which the animation effects should be applied and returns the data in an array. We use a function to save repeating the same code from our Bootstrap API Hooks later on.
  • dropdownEffectStart function: This function triggers the start of an animation (in and out) by applying the animation effect class (ie. .fadeIn) which is passed as the effectToStart argument.
  • dropdownEffectEnd function: This function reacts to the end of an animation (in and out) by removing the animation effect classes (ie. .fadeIn) which are read from the data returned from the dropdownEffectData function above. It also accepts a custom callback function (callbackFunc) which can be trigger after the effect has ended, we use this in our Bootstrap API Hooks later to remove the .open class from the parent .dropdown element once the out animation has run.
  • dropdownSelectors.on call AKA the Bootstrap API Hooks: Here’s where we hook into the Bootstrap API and the dropdown events:
    • show.bs.dropdown: dropdown is triggered to show, so we fire dropdownEffectStart and pass it the “in” animation (pulled from our data-dropdown-in attribute) to apply.
    • shown.bs.dropdown: dropdown has been shown (ie. is open) so we fire dropdownEffectEnd to tiny up after ourselves and remove the applied “in” animation
    • hide.bs.dropdown: dropdown is triggered to hide itself, so we fire dropdownEffectStart again and pass it the “out” animation (pulled from our data-dropdown-out attribute) to apply.

Global Settings

In reality no one wants a different effect of every dropdown, you want consistency right?! That’s where our global settings come in handy.

Some of you with a sharp eye might have noticed a little extra something in the dropdownEffectData() function from the if (parentUl.size() > 0) { line down. These last few lines of code allow you to apply global in and out animations to all dropdown menus within a given ul.nav wrapper element.

In the example below, data-dropdown-in="fadeIn" data-dropdown-out="fadeOut" is added to a wrapper ul.nav element to apply “fadeIn” and “fadeOut” effects to all the .dropdown-menu elements within the wrapper. Further down the example, we have a third dropdown menu which has it’s own data-dropdown-in and data-dropdown-out attributes applied directly to the .dropdown-menu element which overrides the global “fadeIn” and “fadeOut” effects. This might be useful for a special dropdown which needs a bit more flare 🙂

<ul class="nav navbar-nav" data-dropdown-in="fadeIn" data-dropdown-out="fadeOut">
  <!-- using global settings -->
  <li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
      Menu (global setting)
      <span class="caret"></span>
    </a>
    <ul class="dropdown-menu" aria-labelledby="drop1">
      <li><a href="#">Action</a></li>
      <li><a href="#">Another action</a></li>
      <li><a href="#">Something else here</a></li>
    </ul>
  </li>
  <li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
      Menu (global setting)
      <span class="caret"></span>
    </a>
    <ul class="dropdown-menu" aria-labelledby="drop1">
      <li><a href="#">Action</a></li>
      <li><a href="#">Another action</a></li>
      <li><a href="#">Something else here</a></li>
    </ul>
  </li>
  
  <!-- Override global settings -->
  <li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
      Menu (override)
      <span class="caret"></span>
    </a>
    <ul class="dropdown-menu" data-dropdown-in="flipInX" data-dropdown-out="flipOutX">
      <li><a href="#">Action</a></li>
      <li><a href="#">Another action</a></li>
      <li><a href="#">Something else here</a></li>
    </ul>
  </li>
</ul>

CSS

The CSS part is actually completely optional and only needed to apply your own style and tweak the animation effects.

For the tutorial demo we’ve sped up the animations slightly to give the dropdown effects a bit more “snap”!

.dropdown-menu.animated {
  /* Speed up animations */
  -webkit-animation-duration: 0.55s;
  animation-duration: 0.55s;
  -webkit-animation-timing-function: ease;
  animation-timing-function: ease;
}

You can also target specific effects using the CSS selector pattern .dropdown-menu.animated.EFFECTNAME:

.dropdown-menu.animated.fadeIn {
  /*
   * Example of further customisation for the "fadeIn" effect
   * Can be used for all effects ie. .dropdown-menu.animated.EFFECTNAME
   */
  -webkit-animation-duration: 0.45s;
  animation-duration: 0.45s;
  -webkit-animation-timing-function: ease-out;
  animation-timing-function: ease-out;  
}

 

Download & Demo

Credits

License

Use and abuse at your will but attribution & link back to this article is required.

1 Comment

  1. Justin says:

    I know this page is a little old, but I have a question about the jQuery version. I found that using jQuery v. 3.2.1 (https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js) I couldn’t get the dropdown effects to work. I had to use the line in the demo that was jQuery v. 1.11.3 (https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js). Can you tell me why?

Leave a Reply

Your email address will not be published. Required fields are marked *