6

I'm developing a website using bootstrap and its responsive JS+CSS.

At the top of the page I have a fixed navigation bar where an "expand menu" button is shown in case the viewport is too tight. This button does its magic with an animation (a CSS3 one I think) and I'm happy with it, but I would like to do something more (toggle classes with jquery) each time the animation finishes (both the open animation and the close one). I was thinking about a javascript listener (even better by defining it thanks to jquery .on function), but I really don't know what event I should listen to! Any ideas?

UPDATE I've fond out that by listening to this event on the object I wanna control almost does the job well:

$("#main-navbar .nav-collapse").on("transitionend", function(event){    
    console.log("end of the animation");
}

the only problem is that it messes bootstrap animations up on that object: the first time it works, but wen I want to close the expanded navbar, nothing happens (it seems that my listener overrides the bootstrap ones. quite weird, huh?)

Bertuz
  • 2,390
  • 3
  • 25
  • 50
  • I've had trouble with multiple occurrences of animated bootstrap objects, too. In my case, If I have two modals fire one after another, and I take action on the `hidden`, which fire after the hide animation completes, odd things happen with the state of the modal component. I think this logic isn't perfect on the library's end, and switching may be a good option. – SimplGy Jun 06 '13 at 18:07

4 Answers4

6

If you use bootstrap and transitions (css3 transition) you can try this:

$("body").on($.support.transition.end, '#main-navbar .nav-collapse', function(event){    
    console.log("end of the animation");
});

$.support.transition.end contains one of these events: webkitTransitionEnd, transitionend, oTransitionEnd otransitionend, transitionend.

But if you use css3 animation (css3 animation-name and keyframes) you can try this:

$("body").on('webkitAnimationEnd oanimationend msAnimationEnd animationend', '#main-navbar .nav-collapse', function(event){    
    console.log("end of the animation");
});
v.babak
  • 818
  • 11
  • 13
  • 1
    If you are using the second approach it may be better to use one instead of on so that only one event is ever fired – Tom Jun 13 '14 at 14:07
2
$target.on("shown.bs.collapse", function(event){    
    console.log("end of the animation");
});

Wonder that nobody mention about Util.emulateTransitionEnd() which was created on purpose and used by Bootstrap components to catch when the animation is over.

$('#nav')
  .one(
    'bsTransitionEnd', // Util.TRANSITION_END
    handler
  )
  .emulateTransitionEnd(600); // Collapse.TRANSITION_DURATION

In your particular case, you might want to extend corresponding Bootstrap plugin, so that you can figure out which exactly case is yours - Collapse is used by different components.

Here is jsfiddle snippet to demo (based on Bootstrap v4).

jsfiddle.net/ru5ow96z

(function($) {
  var Collapse = $.fn.collapse.Constructor;
  var navbar = $('#nav');

  $.extend(Collapse.Default, {
    navbarClass: ''
  });

  var _show = Collapse.prototype.show;
  Collapse.prototype.show = function() {
    _show.apply(this, Array.prototype.slice.apply(arguments));

    var navbarClass = this._config.navbarClass;
    if (navbarClass && !navbar.hasClass(navbarClass)) {
      navbar.addClass(navbarClass);
    }
  }

  var _hide = Collapse.prototype.hide;
  Collapse.prototype.hide = function() {
    _hide.apply(this, Array.prototype.slice.apply(arguments));

    var navbarClass = this._config.navbarClass;
    if (navbarClass && navbar.hasClass(navbarClass)) {
      navbar
        .one('bsTransitionEnd', function() { // Util.TRANSITION_END
          navbar.removeClass(navbarClass);
        })
        .emulateTransitionEnd(300); // Collapse.TRANSITION_DURATION / 2
    }
  }
})(window.jQuery);
<nav id="nav" role="navigation" class="navbar fixed-top navbar-light bg-light">
  <button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#nav_items" data-navbar-class="navbar-dark bg-dark" aria-expanded="false" aria-label="Menu">
    <span class="text-hide">Menu</span>
    <span class="navbar-toggler-icon"></span>
  </button>

  <div id="nav_items" class="collapse navbar-collapse">
    <div class="nav navbar-nav">
      <a class="nav-item nav-link active" href="">Link <span class="sr-only">Home</span></a>
      <a class="nav-item nav-link" href="">Link2</a>
      <a class="nav-item nav-link" href="">Link3</a>
    </div>
  </div>
</nav>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css" integrity="sha384-Zug+QiDoJOrZ5t4lssLdxGhVrurbmBWopoEl+M6BdEfwnCJZtKxi1KgxUyJq13dy" crossorigin="anonymous">
<style type="text/css">
  button:focus {
    outline-width: 0;
  }

  .navbar-collapse.collapse.show {
    height: 100vh;
  }

  .navbar-nav {
    height: 100vh;
  }

  .navbar-toggler {
    border: none;
    padding: 0.25rem 0;
  }

  .navbar-dark .navbar-toggler-icon {
    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");
  }
</style>

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/js/bootstrap.min.js" integrity="sha384-a5N7Y/aK3qNeh15eJKGWxsqtnX/wWdSZSKp+81YjTmS15nvnvxKHuzaWwXHDli+4" crossorigin="anonymous"></script>
alextunyk
  • 719
  • 9
  • 21
0

jQuery has an animation listener built in: http://docs.jquery.com/Selectors/animated

Example: if( $(foo).is(':animated') ) {...}

Then if an element is !animated you can can apply whatever logic you want.

jusynth
  • 183
  • 7
  • that's not exactly what I need actually, because I wanna listen to the end of any animation. But it could be a good starting point to work on :) – Bertuz Apr 10 '13 at 12:09
  • I've solved it getting around the problem (but the original question still remains: why does listening to an event blocks the animation?). Now I listen to this in order to obtain the same thing I was trying to get: `$('body').on('hidden', '#main-navbar .nav-collapse', function () { console.log('we have hidden some navbar items'); });` – Bertuz Apr 10 '13 at 16:12
  • you would need some kind of callback to let the dom know when the animation completes. You mentioned toggling classes already. So when an element begins animation you could change the class to "isAnimating" and as part of your close animation callback toggle the class to animationFinished. You would also have to reset the class afterwards so it can work again and again. Hope that explanation makes sense if not I'll do a fiddle. http://api.jquery.com/trigger/ could help you out – jusynth Apr 10 '13 at 18:40
0
$('.collapse').on('bsTransitionEnd', function(e) {
  console.log('finished')
})

This worked for me for collapsible panels of any kind. You might try with a different selector.

estani
  • 24,254
  • 2
  • 93
  • 76