Almost every mobile website or mobile app have this hamburger menu button. It creates more space for your app and only visible when you really need it. Today we will look at how to create this button by AngularJS and CSS 3 animation.

Hamburger_icon.svg

Hamburger menu icon

In order to reuse our button for other projects, we should make it as an directive. Here are characteristics of our hamburger button:

  • We should be able to reuse it any time we want.
  • It should be easy to customize the menu content.
  • The open and close animation of menu should be fast. We can achieve this by using CSS3 animation.

You can take a look at demo of the menu at here: DEMO

Let’s start.

Now we create the directive first:

angular
  .module('dt-hamburger-menu', [])
  .directive('dtHamburgerMenu', dtHmburgerMenu);

function dtHmburgerMenu() {
  return {
    restrict: 'E',
    templateUrl: 'dt-hamburger-menu.html',
    controller: dtHmburgerMenuCtrl,
    controllerAs: 'dtHamburgerMenu',
    transclude: true
  };
}

function dtHmburgerMenuCtrl() {
  var vm = this;

  vm.isShowingMenu = false;
  vm.openMenu = openMenu;
  vm.closeMenu = closeMenu;

  function openMenu() {

    vm.isShowingMenu = true;
  }

  function closeMenu() {
    vm.isShowingMenu = false;
  }
}

So in this directive, we only have to method: openMenu and closeMenu. Another important property of this directive is transclude: true. We need to turn on this property to put a <ng-transclude></ng-transclude> in directive template. <ng-transclude> will mark a place which allow to insert content into directive template.

Now the template:

angular
  .module('dt-hamburger-menu')
  .run(['$templateCache', function($templateCache) {
    $templateCache.put("dt-hamburger-menu.html", [
      '<a class="hamburger-menu-button" ng-click="dtHamburgerMenu.openMenu()"></a>',
      '<div class="hamburger-menu-content" ng-class="{active: dtHamburgerMenu.isShowingMenu}">',
        '<div class="hamburger-menu-close-wrapper">',
          '<a class="hamburger-menu-close" ng-click="dtHamburgerMenu.closeMenu()"></a>',
        '</div>',
        '<ng-transclude></ng-transclude>',
      '</div>',
      '<div class="hamburger-backdrop" ng-class="{active: dtHamburgerMenu.isShowingMenu}" ng-click="dtHamburgerMenu.closeMenu()"></div>'
    ].join(''))
  }]);

Because I want to this directive is reusable in any projects so I use $templateCache to put the template of this directive into cache and the cache key is: “dt-hamburger-menu.html”.

There are 3 components in this directive:

  • A button to open menu. When menu is open it will add class “active” to .hamburger-menu-content and .hamburger-backdrop and makes them visible.
  • Menu content, inside menu content we have <ng-transclude> which is the point where we want to insert content of menu
  • A backdrop which is visible when menu is open.

The CSS

We define both appearance and animation of this directive in the css file. I added some comment in css to clarify the usage of each property.

.hamburger-menu-button {
  background-repeat: no-repeat;
  padding-left: 20px;
  background-position: 5px 50%;
  background-size: auto 26px;
  background-image: url('../img/svg/icon-menu.svg'); 
  width: 38px;
  height: 33px;
  display: block;
  margin-top: 12px;
  float: right; /* Menu button is always on the right side of the website */
}

.hamburger-menu-content {
  position: fixed; /* We fix the position of menu */
  top: 0; /* This menu is always on top */
  bottom: 0; /* Make sure that menu is 100% height */
  height: 100%;
  right: 0; 
  width: 250px;
  max-width: 70%; /* It should never ocupy all over the website */
  transform: translate(999px, 0); /* We hide the menu on the right side of the app */
  transition: transform 0.5s; /* We defined the animation at here */
  background: white;
  padding: 20px;  
  z-index: 99;
  box-shadow: 1px 2px 10px 0 rgba(0,0,0,0.5);
}

.hamburger-menu-content.active {
  transform: translate(0, 0); /* We show the menu when "active" class is added */
}

.hamburger-menu-close-wrapper {
  margin-bottom: 30px;
  padding-right: 9px;
}

.hamburger-menu-close-wrapper .hamburger-menu-close {
  float: right;
  background-repeat: no-repeat;
  padding-left: 20px;
  background-position: 5px 50%;
  background-size: auto 40px;
  background-image: url('../img/svg/icon-menu-close.svg');
  width: 40px;
  height: 40px;
  display: block;
  background-position: -2px 50%;
}

.hamburger-menu-content .menu-list {
  padding-right: 20px;
  padding-left: 20px;
}

.hamburger-menu-content .menu-item {
  list-style: none;
  padding: 0;
  margin: 0;
  height: 50px;
  padding: 13px 0px 8px 0px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.3);
  cursor: pointer;
  line-height: 50px;
}

.hamburger-menu-content .menu-item:last-child {
  border-bottom: none;
}

.hamburger-backdrop {
  display: none;
  z-index: 90;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background-color: black;
  opacity: 0.3;
  transition: opacity 0.3s
}

.hamburger-backdrop.active {
  display: block;
}

The animation of our menu content is implemented like this:

  • At normal state, the menu is hidden because of
    transform: translate(999px, 0);
    transition: transform 0.5s;
  • When active class is added we set value of transform property:
    transform: translate(0, 0);

When transition property is defined, browser will automatically calculate the animation of the animated property. Transform property is hardware accelerated so the animation will be smooth.

Put our directive to the website:

Now everything is in place, we can use our directive simple like this:

<dt-hamburger-menu>
  <ul class="menu-list">
    <li class="menu-item" ng-click="vm.testCommand()">
      About
    </li>
    <li class="menu-item" ng-click="vm.testCommand1()">
      Portfolio
    </li>
    <li class="menu-item" ng-click="vm.testCommand2()">
      Contact
    </li>
  </ul>
</dt-hamburger-menu>

Thanks to ng-transclude, we can customize and adapt our menu to any scenario.

Here is the usage of hamburger menu in one of my project:

AngularJS hamburger menu

Hamburger menu in a AngularJS app

I also have created a package for this menu and you can include it to your project: https://github.com/davidtran/dt-hamburger-menu

Let me know if you have feedback.

Thanks.