Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
  1. Web Design
  2. JavaScript

Completing Our Draggable Off-Canvas Menu with GreenSock

This post is part of a series called The Beginner’s Guide to TimelineMax.
Building a Draggable Off-Canvas Menu with GreenSock
Final product image
What You'll Be Creating

In the first part of this Draggable journey, we discussed how to include scripts, investigated the ThrowPropsPlugin, including the requirements to jump start our project in hopes of taking it to eleven! Now, get ready to make an off-canvas menu system which reacts to keyboard and touch.

The Demo

The full demo that we’ll be building and discussing for the remainder of this tutorial is also available on CodePen.

I encourage you to test this for yourself across as many devices as possible, especially keyboard navigation. Each interaction–whether touch, keyboard or mouse–has been accounted for, but as you’ll find in our current landscape you can’t detect a touchscreen and at times trying to do so even results in false positives.

The Setup

Using the markup from part I we’ll begin by adding a container div for structural purposes along with correlating classes for CSS and JavaScript hooks.

Classes that begin with the ”js” prefix signify that these classes only appear in JavaScript; removing them would hinder functionality. They’re never used in CSS, helping to isolate the focus of concerns. The surrounding container will help to control scrolling behavior which is discussed in the upcoming CSS section.


With the the foundation in place it’s time to add a layer of ARIA on top to lend semantic meaning to screen readers and keyboard users.

Since the menu will be hidden by default the aria-hidden attribute is labeled true and will be updated accordingly depending on the menu’s state; false for open, true for closed. Here’s an explanation of the attribute aria-hidden per the W3C specification:

Indicates that the element and all of its descendants are not visible or perceivable to any user as implemented by the author. […] Authors MUST set aria-hidden=”true” on content that is not displayed, regardless of the mechanism used to hide it. This allows assistive technologies or user agents to properly skip hidden elements in the document. ~W3C WAI-ARIA Spec

Authors should be careful what content they hide, making this attribute a separate discussion outside the scope of this article. For those curious, the specification defines the attribute in further length and is somewhat grokkable; something I don’t usually say that often about specification jargon.


Our CSS is where the magic really begins. Let’s take the important parts from the demo that bear meaning and break it down.

Setting the body height to 100% allows the container to stretch the entire viewport, but it’s also playing a more important part; allowing us to hide its overflow.

The overflow scroll fix helps to control how the primary container and navigation behave when either one contains overflowing content. For example, If the container is scrolled—or the menu—the other will not scroll when the user reaches the end of the intially scrolled element. It’s a weird behavior, not typically discussed, but makes for a better user experience.

Viewport Units

Viewport units are really powerful and play a vital role in how the primary container holds overflowing content. Viewport units have wonderful support across browsers these days and I highly suggest you start using them. I’ve used vh units on the nav, but I could have used a percentage instead. During development it was discovered that div.app must use vh units since percentage won’t allow for the overflowing content to maintain typical scrolling behavior; the content results in being clipped. Overflow is set to scroll in preparation in case the menu items exceeed the height of the menu or the height of the viewport becomes narrow.

The .no-js .nav:target provides access to our menu regardless if JavaScript fails or is turned off, hence the reason we added the ID value to the href attribute of the menu trigger.

The primary navigation is moved to the right via a negative margin which is also the same as the nav’s width. For the sake of brevity I’m writing Vanilla CSS, but I’m sure you could write something fancier in a pre-processor of your choice.

The JavaScript

JavaScript is the last stop of this draggable menu journey, but before we write one line of JS we’ll need to write a module pattern setup.


For the configuration setup we’ll define some variables for future reference.

Most of these variables are simply grabbing DOM elements, with the exception of the last two that define our GreenSock targets plus the width of the navigation menu. The utility function getScrollBarWidth() (outside our discussion today) retrieves the width of the scroll bar so we can position the nav just beyond the width of the bar itself in order to see it when the menu opens. The targets are what we move when the menu opens in order to allow adjacent content to be pushed.


To keep things short I’ll only discuss methods that are extremely important to the functionality of the menu behavior. Everything else that you’ll see in the demo not discussed here is the “sugar on top” stuff that makes the menu even more powerful.

The menu function detects whether the container’s x coordinate equals the closed nav state. If so it sets the targets back to their starting position, otherwise sets them to their open position.

This is a utility function to check the menu’s state. This will return 0 if the menu is closed, or a negative value if it’s open.

This is another utility function which sets the target’s x coordinate inside the array parameter of the .set() method to the container’s x position everytime the onDrag or onThrowUpdate event happens. This is part of the Draggable object instance.

These functions help to determine if someone is really selecting text in order to enable / disbale selection capabilities when someone drags across the screen. This is not the most ideal behavior for mouse events, but again, as we already mentioned, you can’t detect a touch screen.

Draggable Instance

As we discussed in the previous tutorial about Draggable, this will create the instance of the Draggable object and target the DOM objects of our choice that can be passed as as an array.

This is our entire Draggable instance and the properties used. The actual demo code contains comments I’ve left in order to understand and gain a better persepective on what each one is responsible for. I encourage you to look into the demo code and even challenge you to deconstruct the why and how.

So Long, But Not Goodbye

This is the end to our GreenSock journey and I hope you learned a ton along the way. A special thanks to Jack and Carl at GreenSock, along with the entire GreenSock community, for their incredible help throughout this series. Last, but not least, a huge thanks to you, the reader for reaching the end of this series; congrats! I hope this series helped gain a better look into the powerful benefits and capabilites of an awesome JavaScript animation library. Build awesome things and stay creative!

Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.