Easy Sliding Drawer In Vanilla JavaScript

Category: Javascript , Menu & Navigation | May 1, 2020
Author: tomaszbujnowicz
Views Total: 283 views
Official Page: Go to website
Last Update: May 1, 2020
License: MIT

Preview:

Easy Sliding Drawer In Vanilla JavaScript

Description:

A dead simple vanilla JavaScript sliding drawer component designed for off-canvas menus, settings panels, sidebar notifications and much more.

How to use it:

1. Create the HTML for the drawer.

<section class="drawer" id="drawer-name" data-drawer-target>
  <div class="drawer__overlay" data-drawer-close tabindex="-1"></div>
  <div class="drawer__wrapper">
    <div class="drawer__header">
      <div class="drawer__title">
        Drawer Title Here
      </div>
      <button class="drawer__close" data-drawer-close aria-label="Close Drawer"></button>
    </div>
    <div class="drawer__content">
      Drawer Content Here
    </div>
  </div>
</section>

2. Hide the drawer on page load.

.drawer {
  display: none;
}

3. The main CSS for the drawer.

.drawer__header {
  padding: 1.5rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid #ddd;
}

.drawer__close {
  margin: 0;
  padding: 0;
  border: none;
  background-color: transparent;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%0A%3Csvg width='15px' height='16px' viewBox='0 0 15 16' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cg id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cg id='2.-Menu' transform='translate(-15.000000, -13.000000)' stroke='%23000000'%3E%3Cg id='Group' transform='translate(15.000000, 13.521000)'%3E%3Cpath d='M0,0.479000129 L15,14.2971819' id='Path-3'%3E%3C/path%3E%3Cpath d='M0,14.7761821 L15,-1.24344979e-14' id='Path-3'%3E%3C/path%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  width: 15px;
  height: 15px;
}

.drawer__wrapper {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  height: 100%;
  width: 100%;
  max-width: 500px;
  z-index: 9999;
  overflow: auto;
  transition: transform 0.3s;
  will-change: transform;
  background-color: #fff;
  display: flex;
  flex-direction: column;
  -webkit-transform: translateX(103%);
  transform: translateX(103%); /* extra 3% because of box-shadow */
  -webkit-overflow-scrolling: touch; /* enables momentum scrolling in iOS overflow elements */
  box-shadow: 0 2px 6px #777;
}

.drawer__content {
  position: relative;
  overflow-x: hidden;
  overflow-y: auto;
  height: 100%;
  flex-grow: 1;
  padding: 1.5rem;
}

.drawer.is-active {
  display: block;
}

.drawer.is-visible .drawer__wrapper {
  -webkit-transform: translateX(0);
  transform: translateX(0);
}

.drawer.is-visible .drawer__overlay {
  opacity: 0.5;
}

4. Style the background overlay when the drawer is opened.

.drawer__overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  z-index: 200;
  opacity: 0;
  transition: opacity 0.3s;
  will-change: opacity;
  background-color: #000;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

5. Create a trigger element to slide the drawer out from the right side of the screen.

<a href="#" data-drawer-trigger aria-controls="drawer-name" aria-expanded="false">Open Drawer</a>

6. The main JavaScript to enable the drawer.

var drawer = function () {

    /**
    * Element.closest() polyfill
    * https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
    */
    if (!Element.prototype.closest) {
      if (!Element.prototype.matches) {
        Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
      }
      Element.prototype.closest = function (s) {
        var el = this;
        var ancestor = this;
        if (!document.documentElement.contains(el)) return null;
        do {
          if (ancestor.matches(s)) return ancestor;
          ancestor = ancestor.parentElement;
        } while (ancestor !== null);
        return null;
      };
    }


    //
    // Settings
    //
    var settings = {
      speedOpen: 50,
      speedClose: 350,
      activeClass: 'is-active',
      visibleClass: 'is-visible',
      selectorTarget: '[data-drawer-target]',
      selectorTrigger: '[data-drawer-trigger]',
      selectorClose: '[data-drawer-close]',

    };


    //
    // Methods
    //

    // Toggle accessibility
    var toggleccessibility = function (event) {
      if (event.getAttribute('aria-expanded') === 'true') {
        event.setAttribute('aria-expanded', false);
      } else {
        event.setAttribute('aria-expanded', true);
      }
    };

    // Open Drawer
    var openDrawer = function (trigger) {

      // Find target
      var target = document.getElementById(trigger.getAttribute('aria-controls'));

      // Make it active
      target.classList.add(settings.activeClass);

      // Make body overflow hidden so it's not scrollable
      document.documentElement.style.overflow = 'hidden';

      // Toggle accessibility
      toggleccessibility(trigger);

      // Make it visible
      setTimeout(function () {
        target.classList.add(settings.visibleClass);
      }, settings.speedOpen);

    };

    // Close Drawer
    var closeDrawer = function (event) {

      // Find target
      var closestParent = event.closest(settings.selectorTarget),
        childrenTrigger = document.querySelector('[aria-controls="' + closestParent.id + '"');

      // Make it not visible
      closestParent.classList.remove(settings.visibleClass);

      // Remove body overflow hidden
      document.documentElement.style.overflow = '';

      // Toggle accessibility
      toggleccessibility(childrenTrigger);

      // Make it not active
      setTimeout(function () {
        closestParent.classList.remove(settings.activeClass);
      }, settings.speedClose);

    };

    // Click Handler
    var clickHandler = function (event) {

      // Find elements
      var toggle = event.target,
        open = toggle.closest(settings.selectorTrigger),
        close = toggle.closest(settings.selectorClose);

      // Open drawer when the open button is clicked
      if (open) {
        openDrawer(open);
      }

      // Close drawer when the close button (or overlay area) is clicked
      if (close) {
        closeDrawer(close);
      }

      // Prevent default link behavior
      if (open || close) {
        event.preventDefault();
      }

    };

    // Keydown Handler, handle Escape button
    var keydownHandler = function (event) {

      if (event.key === 'Escape' || event.keyCode === 27) {

        // Find all possible drawers
        var drawers = document.querySelectorAll(settings.selectorTarget),
          i;

        // Find active drawers and close them when escape is clicked
        for (i = 0; i < drawers.length; ++i) {
          if (drawers[i].classList.contains(settings.activeClass)) {
            closeDrawer(drawers[i]);
          }
        }

      }

    };


    //
    // Inits & Event Listeners
    //
    document.addEventListener('click', clickHandler, false);
    document.addEventListener('keydown', keydownHandler, false);


};

drawer();

7. Slide the drawer out from the left side instead.

<section class="drawer drawer--left" id="drawer-name" data-drawer-target>
  ...
</section>
.drawer--left .drawer__wrapper {
  left: 0;
  right: auto;
  -webkit-transform: translate3d(-100%, 0, 0);
  transform: translate3d(-100%, 0, 0);
}

Changelog:

05/02/2020

  • Use translate3d instead of translateX

04/18/2020

  • Minor adjustments

You Might Be Interested In:


Leave a Reply