Animated Off-canvas Navigation For Bootstrap 4

File Size: 10.7 KB
Views Total: 9144
Last Update:
Publish Date:
Official Website: Go to website
License: MIT
   
Animated Off-canvas Navigation For Bootstrap 4

A jQuery/CSS extension that converts the regular Bootstrap navbar into an off-canvas sidebar push navigation with an animated toggle button.

Built to work with the latest Bootstrap 4 framework and supports multiple navbars.

How to use it:

1. Include the latest jQuery library and Bootstrap 4 framework on the page.

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>

2. Add the class 'navbar-offcanvas' to the Bootstrap navbar.

<nav class="navbar navbar-expand-md navbar-dark bg-dark navbar-offcanvas">
  <div class="container-fluid">
      <a class="navbar-brand" href="#">Navbar</a>
      <ul class="navbar-nav navbar-top">
          <li class="nav-item active">
              <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
          </li>
          <li class="nav-item">
              <a class="nav-link" href="#">Link</a>
          </li>
      </ul>
      <button class="navbar-toggler navbar-toggler-right navbar-icon" type="button" data-toggle="collapse" data-target="#navbar-mobile" aria-controls="navbar-mobile" aria-expanded="false" aria-label="Toggle navigation">
          <span class="icon-bar bar1"></span>
          <span class="icon-bar bar2"></span>
          <span class="icon-bar bar3"></span>
      </button>
      <div class="navbar-collapse collapse ml-auto" id="navbar-mobile">
          <ul class="navbar-nav ml-auto">
              <li class="nav-image">
                  <img src="logo.png" alt="">
              </li>
              <li class="nav-item">
                  <a href="#!" class="nav-link">Link 1</a>
              </li>
              <li class="nav-item">
                  <a href="#!" class="nav-link">Link 2</a>
              </li>
              <li class="nav-item">
                  <a href="#!" class="nav-link">Link 3</a>
              </li>
          </ul>
      </div>
  </div>
</nav>

3. Add the class 'wrapper' to the main content.

<div class="wrapper">
  ...
</div>

4. The main CSS for the off-canvas navigation.

.wrapper { transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); }

.navbar {
  height: 75px;
  transition: all .5s .1s;
}

.navbar-offcanvas { z-index: 1030; }

.navbar-offcanvas .container-fluid {
  position: relative;
  padding: 0;
  transform: translate3d(0px, 0, 0);
  transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1);
}

.navbar-offcanvas .navbar-top { display: none; }
@media (min-width: 992px) {

.navbar-offcanvas .navbar-top {
  display: flex;
  margin-left: auto;
}
}

.navbar-offcanvas .navbar-top .nav-item {
  margin-right: 22px;
  text-align: center;
}

@media (max-width: 991px) {

.navbar-offcanvas .navbar-top .nav-item .nav-link { color: #000; }
}

.navbar-offcanvas .navbar-toggler {
  padding: 0;
  border: 0;
  outline: none;
}

.navbar-offcanvas .navbar-toggler:hover, .navbar-offcanvas .navbar-toggler:focus { cursor: pointer; }

@media (min-width: 768px) {

.navbar-offcanvas .navbar-toggler { display: block; }
}

.navbar-offcanvas .navbar-toggler .icon-bar {
  display: block;
  position: relative;
  width: 24px;
  height: 2px;
  border-radius: 1px;
  background-color: #fff;
}

.navbar-offcanvas .navbar-toggler .icon-bar + .icon-bar { margin-top: 4px; }

.navbar-offcanvas .navbar-toggler .icon-bar.bar1 {
  top: 0;
  outline: 1px solid transparent;
  animation: topbar-back 500ms 0s;
  animation-fill-mode: forwards;
}

.navbar-offcanvas .navbar-toggler .icon-bar.bar2 {
  outline: 1px solid transparent;
  opacity: 1;
}

.navbar-offcanvas .navbar-toggler .icon-bar.bar3 {
  bottom: 0;
  outline: 1px solid transparent;
  animation: bottombar-back 500ms 0s;
  animation-fill-mode: forwards;
}

.navbar-offcanvas .navbar-toggler.toggled .icon-bar.bar1 {
  top: 6px;
  animation: topbar-x 500ms 0s;
  animation-fill-mode: forwards;
}

.navbar-offcanvas .navbar-toggler.toggled .icon-bar.bar2 { opacity: 0; }

.navbar-offcanvas .navbar-toggler.toggled .icon-bar.bar3 {
  bottom: 6px;
  animation: bottombar-x 500ms 0s;
  animation-fill-mode: forwards;
}

.navbar-offcanvas .navbar-collapse.collapse, .navbar-offcanvas .navbar-collapse.collapse.in, .navbar-offcanvas .navbar-collapse.collapsing { display: none !important; }

.nav-open .navbar-collapse { transform: translate3d(0px, 0, 0); }

.nav-open .navbar > .container-fluid { transform: translate3d(-424px, 0, 0); }

@media (max-width: 991px) {

.nav-open .navbar > .container-fluid { transform: translate3d(-282.6666666667px, 0, 0); }
}

.nav-open .wrapper { transform: translate3d(-150px, 0, 0); }

body > .navbar-collapse {
  display: block !important;
  position: fixed;
  top: 0;
  right: -10px;
  width: 424px;
  height: 100%;
  padding: 60px 1rem;
  background-color: #fff;
  border-left: 1px solid #e3e3e3;
  text-align: center;
  visibility: visible;
  overflow-y: visible;
  transform: translate3d(424px, 0, 0);
  transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1);
  z-index: 1032;
}

body > .navbar-collapse:after {
  content: "";
  position: absolute;
  top: 28px;
  left: -20px;
  border-left: 10px solid #fff;
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  border-right: 10px solid transparent;
  transform: rotate(180deg);
  transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1);
  z-index: 1032;
}

@media (max-width: 991px) {

body > .navbar-collapse { width: 282.6666666667px; }
}

body > .navbar-collapse .nav-image { margin-bottom: 65px; }

body > .navbar-collapse .nav-image img {
  display: block;
  margin: 0 auto;
  border: 1px solid rgba(0, 0, 0, 0.14);
  width: 91px;
  height: 69px;
}

body > .navbar-collapse .navbar-top {
  margin: 0 !important;
  flex-direction: column;
}

@media (min-width: 992px) {

body > .navbar-collapse .navbar-top { display: none; }
}

body > .navbar-collapse .navbar-top li { text-align: center; }

body > .navbar-collapse .navbar-top li a {
  display: block;
 padding: .5rem 1rem;
  font-weight: 700;
  color: #000;
}

body > .navbar-collapse .navbar-top li a:hover, body > .navbar-collapse .navbar-top li a:focus { text-decoration: none; }

body > .navbar-collapse .nav-link, body > .navbar-collapse .dropdown-toggle {
  font-weight: 700;
  color: #222;
  transition: color .2s ease-out;
}

body > .navbar-collapse .nav-link:hover, body > .navbar-collapse .nav-link:focus, body > .navbar-collapse .dropdown-toggle:hover, body > .navbar-collapse .dropdown-toggle:focus { text-decoration: none; }

body > .navbar-collapse .nav-link.disabled, body > .navbar-collapse .dropdown-toggle.disabled { color: rgba(0, 0, 0, 0.35); }

body > .navbar-collapse .nav-link.disabled:hover, body > .navbar-collapse .nav-link.disabled:focus, body > .navbar-collapse .dropdown-toggle.disabled:hover, body > .navbar-collapse .dropdown-toggle.disabled:focus { cursor: not-allowed; }
 @media (min-width: 992px) {

body > .navbar-top { display: none; }
}

body > #overlay {
  content: "";
  position: fixed;
  top: 0;
  left: auto;
  right: calc(282.6666666667px - 10px);
  width: 100%;
  height: 100%;
  opacity: 0;
  overflow-x: hidden;
  z-index: 1029;
}

@media (min-width: 992px) {

body > #overlay { right: calc(424px - 10px); }
}

@keyframes 
topbar-x {  0% {
 top: 0px;
 transform: rotate(0deg);
}
 45% {
 top: 6px;
 transform: rotate(145deg);
}
 75% {
 transform: rotate(130deg);
}
 100% {
 transform: rotate(135deg);
}
}

@keyframes 
topbar-back {  0% {
 top: 6px;
 transform: rotate(135deg);
}
 45% {
 transform: rotate(-10deg);
}
 75% {
 transform: rotate(5deg);
}
 100% {
 top: 0px;
 transform: rotate(0);
}
}

@keyframes 
bottombar-x {  0% {
 bottom: 0px;
 transform: rotate(0deg);
}
 45% {
 bottom: 6px;
 transform: rotate(-145deg);
}
 75% {
 transform: rotate(-130deg);
}
 100% {
 transform: rotate(-135deg);
}
}

@keyframes 
bottombar-back {  0% {
 bottom: 6px;
 transform: rotate(-135deg);
}
 45% {
 transform: rotate(10deg);
}
 75% {
 transform: rotate(-5deg);
}
 100% {
 bottom: 0px;
 transform: rotate(0);
}
}

5. The main JavaScript to activate the off-canvas navigation.

var window_height;
var window_width;
var navbar_initialized = false;
var nav_toggle;

var offCanvas = {
    sidenav: {
        // Sidenav is not visible by default.
        // Change to 1 if necessary
        sidenav_visible: 0
    },
    initSideNav: function initSideNav() {
        if (!navbar_initialized) {
            var $nav = $('nav');

            // Add the offcanvas class to the navbar if it's not initialized
            $nav.addClass('navbar-offcanvas');

            // Clone relevant navbars
            var $navtop = $nav.find('.navbar-top').first().clone(true);
            var $navbar = $nav.find('.navbar-collapse').first().clone(true);

            // Let's start with some empty vars
            var ul_content = '';
            var top_content = '';

            // Set min-height of the new sidebar to the screen height
            $navbar.css('min-height', window.screen.height);

            // Take the content of .navbar-top
            $navtop.each(function() {
                var navtop_content = $(this).html();
                top_content = top_content + navtop_content;
            });

            // Take the content of .navbar-collapse
            $navbar.children('ul').each(function() {
                var nav_content = $(this).html();
                ul_content = ul_content + nav_content;
            });

            // Wrap the new content inside an <ul>
            ul_content = '<ul class="navbar-nav sidebar-nav">' + ul_content + '</ul>';

            // Insert the html content into our cloned content
            $navbar.html(ul_content);
            $navtop.html(top_content);

            // Append the navbar to body,
            // and insert the content of the navicons navbar just below the logo/nav-image
            $('body').append($navbar);
            $('.nav-image').after($navtop);


            // Set the toggle-variable to the Bootstrap navbar-toggler button
            var $toggle = $('.navbar-toggler');

            // Add/remove classes on toggle and set the visiblity of the sidenav,
            // and append the overlay. Also if the user clicks the overlay,
            // the sidebar will close
            $toggle.on('click', function () {
                if (offCanvas.sidenav.sidenav_visible == 1) {
                    $('html').removeClass('nav-open');
                    offCanvas.sidenav.sidenav_visible = 0;
                    $('#overlay').remove();
                    setTimeout(function() {
                        $toggle.removeClass('toggled');
                    }, 300);
                } else {
                    setTimeout(function() {
                        $toggle.addClass('toggled');
                    }, 300);

                    // Add the overlay and make it close the sidenav on click
                    var div = '<div id="overlay"></div>';
                    $(div).appendTo("body").on('click', function() {
                        $('html').removeClass('nav-open');
                        offCanvas.sidenav.sidenav_visible = 0;
                        $('#overlay').remove();
                        setTimeout(function() {
                            $toggle.removeClass('toggled');
                        }, 300);
                    });

                    $('html').addClass('nav-open');
                    offCanvas.sidenav.sidenav_visible = 1;
                }
            });
            // Set navbar to initialized
            navbar_initialized = true;
        }
    }
};

$(document).ready(function () {
    window_width = $(window).width();

    nav_toggle = $('nav').hasClass('navbar-offcanvas') ? true : false;

    // Responsive checks
    if (window_width < 992 || nav_toggle) {
        offCanvas.initSideNav();
    }

    // Close the sidebar if the user clicks a link or a dropdown-item,
    // and close the sidebar
    $('.nav-link:not(.dropdown-toggle), .dropdown-item').on('click', function () {
        var $toggle = $('.navbar-toggler');

        $('html').removeClass('nav-open');
        offCanvas.sidenav.sidenav_visible = 0;
        setTimeout(function () {
            $toggle.removeClass('toggled');
        }, 300);
    });
});

$(window).resize(function () {
    window_width = $(window).width();

    // More responsive checks if the user resize the browser
    if (window_width < 992) {
        offCanvas.initSideNav();
    }

    if (window_width > 992 && !nav_toggle) {
        $('nav').removeClass('navbar-offcanvas');
        offCanvas.sidenav.sidenav_visible = 1;
        navbar_initialized = false;
    }
});

Changelog:

2019-06-19

  • Fixed for Bootstrap 4.3+

This awesome jQuery plugin is developed by Morten Sørensen. For more Advanced Usages, please check the demo page or visit the official website.