Skip to content

Instantly share code, notes, and snippets.

@JBreit
Created March 14, 2016 20:50
Show Gist options
  • Save JBreit/7c65354b674c6ddf1702 to your computer and use it in GitHub Desktop.
Save JBreit/7c65354b674c6ddf1702 to your computer and use it in GitHub Desktop.
ES6 Modal Menu
<nav class="top-nav">
<p>Click on the 'Navigate' button to open modal.</p>
<div id="burger-menu" class="burger-menu">
<div id="burger-click-region" class="burger-click-region">
<span class="burger-menu-piece"></span>
<span class="burger-menu-piece"></span>
<span class="burger-menu-piece"></span>
</div>
<span class="burger-menu-txt">Navigate</span>
</div>
<div class="ks-modal-contents">
<nav id="site-menu-contents" class="site-nav">
<ul class="site-nav-list">
<li><a href="#">Skills</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Work</a></li>
<li><a href="#">Blog</a></li>
</ul>
</nav>
</div>
</nav> <!-- .top-nav -->
class KS_Modal {
constructor(selectorName, modalContentsSelector, clickRegionElement=null) {
this.self = document.querySelector(selectorName);
this.modalContents = document.querySelector(modalContentsSelector);
this.clickRegionElement = document.querySelector(clickRegionElement);
this.isOpen = false;
this.openDelay = 500;
this.openDelayTimer = null;
// Initialize modal
this._init();
}
_init() {
// Create node ID to hold the markup for this modal instance
var modalNodeInstance = document.createElement('div');
modalNodeInstance.id = 'ks-modal-instance-' + Math.floor(Math.random()*100000);
// Create modal container element
this.modalEle = document.createElement('div');
this.modalEle.classList.add('ks-modal');
// Create modal inner container
var modalInnerEle = document.createElement('div');
modalInnerEle.classList.add('ks-modal-inner');
// Clone modal contents into inner modal container
var modalContents = this.modalContents.cloneNode(true);
modalInnerEle.appendChild(modalContents);
// Add modal node instance to the DOM
document.body.appendChild(modalNodeInstance);
// Add inner container as a child to the modal element
this.modalEle.appendChild(modalInnerEle);
// Add modal element to the instance container
modalNodeInstance.appendChild(this.modalEle);
// Get click region element
var clickRegionElement;
if(this.clickRegionElement === null) {
clickRegionElement = this.self;
}
else {
clickRegionElement = this.clickRegionElement;
}
// Add event listeners for modal
clickRegionElement.addEventListener('click', () => {
if(this.isOpen) {
this.close();
}
else {
this.open();
}
});
}
open() {
// Only open the modal if delay timer expired
if(this.openDelayTimer === null) {
this.isOpen = true;
this.self.classList.add('is-open');
this.modalEle.classList.add('open');
if(this.clickRegionElement !== null) {
this.clickRegionElement.classList.add('active');
}
this.openDelayTimer = setTimeout( () => {
clearTimeout(this.openDelayTimer);
this.openDelayTimer = null;
}, this.openDelay);
}
}
close() {
// Only close the modal if delay timer expired
if(this.openDelayTimer === null) {
this.isOpen = false;
this.self.classList.remove('is-open');
this.modalEle.classList.remove('open');
if(this.clickRegionElement !== null) {
this.clickRegionElement.classList.remove('active');
this.clickRegionElement.classList.add('closing');
}
this.openDelayTimer = setTimeout( () => {
if(this.clickRegionElement !== null) {
this.clickRegionElement.classList.remove('closing');
}
clearTimeout(this.openDelayTimer);
this.openDelayTimer = null;
}, this.openDelay);
}
}
}
// Create modal instance...
var menuModal = new KS_Modal('#burger-menu', '#site-menu-contents', '#burger-click-region');
@import "bourbon";
@import url(http://fonts.googleapis.com/css?family=Montserrat);
body {
background-color: #1a1a1a;
font-family: 'Montserrat', sans-serif;
}
.top-nav {
position: relative;
height: 60px;
background-color: black;
}
.top-nav p {
position: absolute;
left: 20px;
top: 8px;
color: #666;
font-size: 14px;
}
.burger-menu {
position: absolute;
right: 20px;
top: 16px;
width: 140px;
height: 30px;
color: #60635E;
text-transform: uppercase;
z-index: 100;
&.is-open {
z-index: 1010;
}
}
.burger-click-region {
position: absolute;
left: 100px;
width: 40px;
height: 30px;
cursor: pointer;
}
.burger-menu-txt {
display: block;
position: absolute;
left: 0;
top: 5px;
cursor: default;
transition: opacity 200ms ease-out, transform 200ms cubic-bezier(.34, .55, .25, .83);
.is-open & {
opacity: 0;
transform: translate3d(-50px, 0, 0);
}
}
$menu-animation-duration: 400ms;
$menu-animation-timing: ease-out;
.burger-menu-piece {
display: block;
position: absolute;
width: 40px;
border-top: 6px solid #C2C2C2;
transform-origin: 50% 50%;
transition: transform $menu-animation-duration $menu-animation-timing;
&:nth-child(1) {
top: 0;
}
&:nth-child(2) {
top: 12px;
opacity: 1;
transition: transform $menu-animation-duration $menu-animation-timing, opacity 0ms linear $menu-animation-duration / 2;
}
&:nth-child(3) {
top: 24px;
}
.active & {
&:nth-child(1) {
animation: burger-open-top $menu-animation-duration $menu-animation-timing forwards;
}
&:nth-child(2) {
opacity: 0;
transition: transform $menu-animation-duration $menu-animation-timing, opacity 0ms linear $menu-animation-duration / 2;
}
&:nth-child(3) {
animation: burger-open-bot $menu-animation-duration $menu-animation-timing forwards;
}
}
.closing & {
&:nth-child(1) {
animation: burger-close-top $menu-animation-duration $menu-animation-timing forwards;
}
&:nth-child(3) {
animation: burger-close-bot $menu-animation-duration $menu-animation-timing forwards;
}
}
}
@keyframes burger-open-top {
50% {
transform: translate3d(0, 12px, 0);
}
100% {
transform: translate3d(0, 12px, 0) rotate(45deg);
}
}
@keyframes burger-open-bot {
50% {
transform: translate3d(0, -12px, 0);
}
100% {
transform: translate3d(0, -12px, 0) rotate(-45deg);
}
}
@keyframes burger-close-top {
0% {
transform: translate3d(0, 12px, 0) rotate(45deg);
}
50% {
transform: translate3d(0, 12px, 0) rotate(0deg);
}
100% {
transform: translate3d(0, 0, 0);
}
}
@keyframes burger-close-bot {
0% {
transform: translate3d(0, -12px, 0) rotate(-45deg);
}
50% {
transform: translate3d(0, -12px, 0) rotate(0deg);
}
100% {
transform: translate3d(0, 0, 0);
}
}
.ks-modal {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
opacity: 0;
visibility: hidden;
z-index: 1000;
transition: opacity 500ms ease-out, visibility 0 linear 500ms;
&.open {
opacity: 1;
visibility: visible;
transition: opacity 500ms ease-out, visibility 0 linear 0;
}
}
.ks-modal-contents {
display: none;
}
.site-nav-list {
position: absolute;
top: 15%;
width: 100%;
list-style: none;
padding: 0;
margin: 0;
li {
$menu-item-1-delay: 140ms;
$menu-item-2-delay: 100ms;
$menu-item-3-delay: 70ms;
$menu-item-4-delay: 50ms;
margin: 20px 0;
opacity: 0;
transform: translate3d(0, 50px, 0);
&:nth-child(1) {
transition: opacity 250ms ease-out $menu-item-1-delay, transform 250ms ease-in $menu-item-1-delay;
}
&:nth-child(2) {
transition: opacity 250ms ease-out $menu-item-2-delay, transform 250ms ease-in $menu-item-2-delay;
}
&:nth-child(3) {
transition: opacity 250ms ease-out $menu-item-3-delay, transform 250ms ease-in $menu-item-3-delay;
}
&:nth-child(4) {
transition: opacity 500ms ease-out $menu-item-4-delay, transform 400ms ease-out $menu-item-4-delay;
}
}
li a {
display: block;
color: white;
font-size: 40px;
text-align: center;
text-decoration: none;
transition: color 200ms ease-in;
&:hover {
color: magenta;
}
}
}
.ks-modal.open {
.site-nav-list li {
$menu-item-1-delay: 250ms;
$menu-item-2-delay: 270ms;
$menu-item-3-delay: 300ms;
$menu-item-4-delay: 340ms;
opacity: 1;
transform: translate3d(0, 0, 0);
&:nth-child(1) {
transition: opacity 500ms ease-out $menu-item-1-delay, transform 400ms ease-out $menu-item-1-delay;
}
&:nth-child(2) {
transition: opacity 500ms ease-out $menu-item-2-delay, transform 400ms ease-out $menu-item-2-delay;
}
&:nth-child(3) {
transition: opacity 500ms ease-out $menu-item-3-delay, transform 400ms ease-out $menu-item-3-delay;
}
&:nth-child(4) {
transition: opacity 500ms ease-out $menu-item-4-delay, transform 400ms ease-out $menu-item-4-delay;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment