I am trying to make a toggle menu for my site, but when I open the menu everything jumps some pixels to the left. I have played with some settings, but nothing worked. I made some small changes to the original CSS for the navigation, to make the button and the ul to align. The menu should be opening the list straight down, not affecting the button's and menu's position.
Is there a better way to set up the js function or can it be fixed in CSS? I used the built-in navigation function from WordPress starter theme Underscores.
( function() {
const siteNavigation = document.getElementById( 'site-navigation' );
// Return early if the navigation don't exist.
if ( ! siteNavigation ) {
return;
}
const button = siteNavigation.getElementsByTagName( 'button' )[ 0 ];
// Return early if the button don't exist.
if ( 'undefined' === typeof button ) {
return;
}
const menu = siteNavigation.getElementsByTagName( 'ul' )[ 0 ];
// Hide menu toggle button if menu is empty and return early.
if ( 'undefined' === typeof menu ) {
button.style.display = 'none';
return;
}
if ( ! menu.classList.contains( 'nav-menu' ) ) {
menu.classList.add( 'nav-menu' );
}
// Toggle the .toggled class and the aria-expanded value each time the button is clicked.
button.addEventListener( 'click', function() {
siteNavigation.classList.toggle( 'toggled' );
if ( button.getAttribute( 'aria-expanded' ) === 'true' ) {
button.setAttribute( 'aria-expanded', 'false' );
} else {
button.setAttribute( 'aria-expanded', 'true' );
}
} );
// Remove the .toggled class and set aria-expanded to false when the user clicks outside the navigation.
document.addEventListener( 'click', function( event ) {
const isClickInside = siteNavigation.contains( event.target );
if ( ! isClickInside ) {
siteNavigation.classList.remove( 'toggled' );
button.setAttribute( 'aria-expanded', 'false' );
}
} );
// Get all the link elements within the menu.
const links = menu.getElementsByTagName( 'a' );
// Get all the link elements with children within the menu.
const linksWithChildren = menu.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' );
// Toggle focus each time a menu link is focused or blurred.
for ( const link of links ) {
link.addEventListener( 'focus', toggleFocus, true );
link.addEventListener( 'blur', toggleFocus, true );
}
// Toggle focus each time a menu link with children receive a touch event.
for ( const link of linksWithChildren ) {
link.addEventListener( 'touchstart', toggleFocus, false );
}
/**
* Sets or removes .focus class on an element.
*/
function toggleFocus() {
if ( event.type === 'focus' || event.type === 'blur' ) {
let self = this;
// Move up through the ancestors of the current link until we hit .nav-menu.
while ( ! self.classList.contains( 'nav-menu' ) ) {
// On li elements toggle the class .focus.
if ( 'li' === self.tagName.toLowerCase() ) {
self.classList.toggle( 'focus' );
}
self = self.parentNode;
}
}
if ( event.type === 'touchstart' ) {
const menuItem = this.parentNode;
event.preventDefault();
for ( const link of menuItem.parentNode.children ) {
if ( menuItem !== link ) {
link.classList.remove( 'focus' );
}
}
menuItem.classList.toggle( 'focus' );
}
}
}() );
.main-navigation {
display: block;
width: 100%;
padding-right: 0;
}
.main-navigation ul {
display: none;
list-style: none;
margin: 0;
padding-left: 0;
text-align: right;
}
.main-navigation ul li:hover > ul,
.main-navigation ul li.focus > ul {
left: auto;
}
.main-navigation li {
position: relative;
top: 10px;
left: -40px;
}
.main-navigation a {
display: block;
text-decoration: none;
}
/* Small menu. */
.menu-toggle,
.main-navigation.toggled ul {
display: block;
}
.main-navigation.toggled{
height: 170px;
}
/* CUSTOM HEADER STYLES */
.site-header {
display: block;
margin: 0 30px;
padding: 50px 0;
}
.site-branding p {
float: left;
font-family: 'Merriweather Sans', sans-serif;
font-size: 1.5em;
margin: 0;
}
.site-branding p a {
text-decoration: none;
}
/* Navigation */
nav.main-navigation {
width: auto;
float: right;
<header id="masthead" class="site-header">
<div class="site-branding">
<?php
the_custom_logo();
if ( is_front_page() && is_home() ) :
?>
<h1 class="site-title"><?php bloginfo( 'name' ); ?></h1>
<?php
else :
?>
<p class="site-title"><?php bloginfo( 'name' ); ?></p>
<?php
endif; ?>
</div><!-- .site-branding -->
<nav id="site-navigation" class="main-navigation">
<button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false"><?php esc_html_e( '☰', 'ab-portfolio' ); ?></button>
<?php
wp_nav_menu(
array(
'theme_location' => 'menu-1',
'menu_id' => 'primary-menu',
)
);
?>
</nav><!-- #site-navigation -->
</header><!-- #masthead -->
Closed menu
Open menu
This is because the content of your <ul> is bigger than the button itself and it always tries to align on the left.
You can simply fix it adding a float: right; to the button.
.menu-toggle {
float: right;
}
Alternatively you can try add some margin-left and margin-right to the button, like this:
.menu-toggle {
margin: 0 8px;
}
In this last case you can skip the float: right;.
Here's a working Codepen: https://codepen.io/alezuc/pen/QWyKZLX
Hope this helps!
Related
I have a list of several elements, and some controlling buttons that can hide/show these elements. Some buttons have control over just one element, while others have multiple elements.
What does my code do:
Button01 hides/shows ElementX and ElementY,
Button02 hides/shows only ElementY.
Once Button01 is clicked, both of the elements are hidden, and clicking Button02 doesn't change anything until Button01 is clicked again.
What I want to do:
Once Button01 hides ElementX and ElementY, Button02 must also be grayed out automatically because its associated element is gone.
And then, clicking Button02 should bring ElementY back and enable Button01 too since one of the associated elements of Button01 is back.
for (let button of document.querySelectorAll(".filterbutton")) {
button.addEventListener("click", filter);
}
let filters = new Set;
function toggleDisplay(selector, display) {
let elems = document.querySelectorAll(selector);
for (let elem of elems) {
elem.style.display = display;
}
}
function filter() {
let filterSelector = this.dataset.filter;
let show = filters.delete(filterSelector);
this.style.color = show ? "" : "rgb(200,200,200)";
if (!show) {
filters.add(filterSelector); // toggle this filter
} else {
toggleDisplay(filterSelector, "");
}
if (filters.size) {
toggleDisplay([...filters].join(","), "none");
}
}
<div class="filterbutton" data-filter=".filter01">Filter01</div>
<div class="filterbutton" data-filter=".filter02">Filter02</div>
<div class="filter01">ElementX</div>
<div class="filter01 filter02">ElementY</div>
You would need to compare the shown elements with the filters on each button when a button gets clicked. See the solution below
const btns = Array.from( document.getElementsByClassName( 'filterbutton' ) );
const els = Array.from( document.getElementsByClassName( 'element' ) );
document.addEventListener( 'click', function( event ) {
const target = event.target.parentElement;
if ( target.hasAttribute('data-filter') ) {
const filter = target.getAttribute( 'data-filter' ).split(" ");
/* If button is active remove matching elements else show */
!target.classList.contains( 'hide' )
? filter.forEach( el => els[el - 1].classList.add( 'hide' ) )
: filter.forEach( el => els[el - 1].classList.remove( 'hide' ) );
btns.forEach( btn => {
const filter = btn.getAttribute( 'data-filter' ).split(" ");
/* Empty array to push true/false if buttons matching elements are visible */
const matches = [];
filter.forEach( match => matches.push( els[match - 1].classList.contains( 'hide' ) ) );
/* If any matches are visible button is active */
matches.includes( false )
? btn.classList.remove( 'hide' )
: btn.classList.add( 'hide' );
});
}
});
* { box-sizing: border-box } body { font-family: monospace; margin: 0 } hr { margin: 1em 0 }
:root { --transTime: .25s; --transFunction: ease-in-out }
#filters, #elements { display: flex; gap: 1em }
.filterbutton, .element {
--trans: opacity var(--transTime) var(--transFunction);
transition: var(--trans); -o-transition: width var(--trans); -moz-transition: var(--trans); -webkit-transition: var(--trans);
}
.filterbutton {
border: 1px solid currentColor;
border-radius: .375em;
color: black;
font-family: inherit;
padding: .5em 1em;
position: relative;
}
.filterbutton.hide { opacity: .5 }
.filterbutton span::before {
border-radius: .375em;
content: "";
cursor: pointer;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: 1;
}
.filterbutton span:first-of-type { display: inline }
.filterbutton span:last-of-type { display: none }
.filterbutton.hide span:first-of-type { display: none }
.filterbutton.hide span:last-of-type { display: inline }
.element {
background: lightgrey;
flex: 1 0 0%;
padding: 1em;
text-align: center;
}
.element.hide { opacity: 0 }
<div id="filters">
<button class="filterbutton" type="button" data-filter="1 2 3 4">
<span>Hide</span><span>Show</span> All
</button>
<button class="filterbutton" type="button" data-filter="1">
<span>Hide</span><span>Show</span> 1
</button>
<button class="filterbutton" type="button" data-filter="2 3">
<span>Hide</span><span>Show</span> 2 & 3
</button>
<button class="filterbutton" type="button" data-filter="4">
<span>Hide</span><span>Show</span> 4
</button>
</div>
<hr>
<div id="elements">
<article class="element">Element 1</article>
<article class="element">Element 2</article>
<article class="element">Element 3</article>
<article class="element">Element 4</article>
</div>
Since you have not given a specific problem that can be expressed by code, the answer can only be given algorithmically:
Аdd class active to the visible elements. after clicking the filter button, remove this class from the filtered elements. at the end of filtering, go through all the buttons and if the result is document.querySelectorAll('.active.filter') empty set - disabled the filter button.
I was able to successfully add code to my script.js file so that the links in the nav section would highlight as you scrolled down the page.
But ever since I converted my webpage into a wordpress template, the javascript that allowed the highlighting nav links no longer works.
I have played around w/ the code to try and get it to work again, but nothing works.
I do not know how this is supposed to work now that I am attempting to do the same thing in wordpress.
Below is the old code. Any help is greatly appreciated.
/* Script.js */
const sections = document.querySelectorAll('section');
const navLi = document.querySelectorAll('nav .container ul li');
window.addEventListener('scroll', () => {
let current = '';
sections.forEach( section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (pageYOffset >= sectionTop) {
current = section.getAttribute('id');
}
});
navLi.forEach( li => {
if (pageYOffset <= 1710) {
li.classList.remove('active-section');
if( li.classList.contains(current) ) {
li.classList.add('active-section');
}
} else {
current = 'contact';
li.classList.remove('active-section');
if( li.classList.contains(current = 'contact') ) {
li.classList.add('active-section');
}
}
});
});
<!-- header.php -->
<nav id="nav" <?php echo (is_admin_bar_showing()) ? ' style="top: 32px;" ' : ''; ?>>
<div class="container">
<!-- hamburger menu -->
<input type="checkbox" id="check">
<label for="check" class="checkbtn">
<i class="fas fa-bars"></i>
</label>
<!-- nav-logo -->
<p id="logo">lf</p>
<!-- nav links -->
<?php
//
if( has_nav_menu( "port-nav-menu" )) {
wp_nav_menu(array(
"theme_location" => "port-nav-menu",
"container" => ""
));
}
?>
</div>
</nav>
/* style.css */
nav {
display: flex;
position: fixed;
text-transform: uppercase;
top: 0%;
width: 100%;
z-index: 1;
}
nav a:link, nav a:visited {
color: #ffffff;
text-decoration: none;
}
nav .container ul li.active-section {
background: #e31b6d;
}
nav a:hover {
color: #e31b6d;
}
nav ul {
display: flex;
float: right;
line-height: 50px;
list-style-type: none;
margin: auto;
}
I need your help. I'm currently trying to make a sortable list of items. Each item can also be a child item of another one:
(function ( $ ) {
$( document ).ready( function () {
$( '#import-sort-wrapper' ).sortable( {
placeholder: 'sortable-placeholder',
connectWith: '.import-sort-item-variations',
start: function ( e, ui ) {
ui.placeholder.height( ui.item.height() );
},
beforeStop: function ( e, ui ) {
console.log( 'before stop' );
// Check if product has already variations
if (ui.item.find( '.import-sort-item-variations:first li' ).length > 0) {
$( ui.sender ).sortable( 'cancel' );
}
},
stop: function ( e, ui ) {
let itemVariations = ui.item.find( '.import-sort-item-variations:first' );
// Check if item is variable product and display / hide variations list
if (ui.item.closest( '.import-sort-item-variations' ).length === 1) {
// itemVariations.empty();
itemVariations.hide();
} else {
itemVariations.show();
}
}
} );
$( '.import-sort-item-variations' ).sortable( {
placeholder: 'sortable-placeholder',
connectWith: '#import-sort-wrapper',
start: function ( e, ui ) {
ui.placeholder.height( ui.item.height() );
},
stop: function ( e, ui ) {
let itemVariations = ui.item.find( '.import-sort-item-variations:first' );
// Show variation option again in case item is moved out of variation list
if (ui.item.closest( '.import-sort-item-variations' ).length === 0) {
itemVariations.show();
}
}
} );
} );
});
#import-sort-wrapper {
text-align: left;
display: flex;
flex-direction: column;
list-style-type: none;
margin: 0;
padding: 0;
}
#import-sort-wrapper li {
font-size: 14px;
line-height: 1.5;
position: relative;
padding: 10px 15px;
margin: 9px 0 0;
cursor: move;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
box-sizing: border-box;
}
#import-sort-wrapper li.import-sort-item {
background: #f6f7f7;
border: 1px solid #dcdcde;
}
#import-sort-wrapper li.import-sort-item .import-sort-item-header .sort-item-header-id {
font-weight: 600;
}
#import-sort-wrapper li.import-sort-item .import-sort-item-header .sort-item-header-type {
color: #50575e;
font-style: italic;
font-weight: 400;
margin-left: 4px;
font-size: 13px;
}
#import-sort-wrapper li.import-sort-item .import-sort-item-variations {
border: 1px solid #dcdcde;
min-height: 30px;
background: #ffffff;
margin-top: 10px;
}
#import-sort-wrapper li.import-sort-item .import-sort-item-variations li {
margin: 10px;
}
#import-sort-wrapper li.ui-sortable-placeholder {
border: 1px dashed #c3c4c7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<ul id="import-sort-wrapper">
<li class="import-sort-item">
<span class="import-sort-item-header">
<span class="sort-item-header-id">30:</span>
<span class="sort-item-header-name">Testprodukt</span>
<span class="sort-item-header-type">Variables Produkt</span>
</span>
<ul class="import-sort-item-variations">
</ul>
</li>
<li class="import-sort-item">
<span class="import-sort-item-header">
<span class="sort-item-header-id">28:</span>
<span class="sort-item-header-name">Erdbeeren</span>
<span class="sort-item-header-type">Variables Produkt</span>
</span>
<ul class="import-sort-item-variations">
</ul>
</li>
<li class="import-sort-item">
<span class="import-sort-item-header">
<span class="sort-item-header-id">29:</span>
<span class="sort-item-header-name">Variables Produkt</span>
<span class="sort-item-header-type">Variables Produkt</span>
</span>
<ul class="import-sort-item-variations">
</ul>
</li>
</ul>
Now I somehow want to prevent any drag-n-drop of an element which already has some childs into another main element. It should only be possible if the element which should be dragged has no childs! I've tried checking some things inside the beforeStop callback, but somehow I don't get this thing working correctly. Any ideas? Maybe I'm on the wrong way and this is not possible...
if you change your code
beforeStop: function ( e, ui ) {
console.log( 'before stop' );
// Check if product has already variations
if (ui.item.find( '.import-sort-item-variations:first li' ).length > 0) {
$( ui.sender ).sortable( 'cancel' );
}
},
To :
beforeStop: function ( e, ui ) {
console.log( 'before stop' );
// Check if product has already variations
if (ui.item.find( '.import-sort-item-variations:first li' ).length > 0) {
$( '#import-sort-wrapper' ).sortable('cancel')
}
},
Is that what you want? ?
I've found the solution. Thanks to Funky for the hint. Also I've moved the function to the child sortable and used a different callback received since sortable('cancel') causes some JS errors when used in beforeStop:
receive: function ( e, ui ) {
console.log( ui.sender );
// Check if product has already variations
if (ui.item.find( '.import-sort-item-variations:first li' ).length > 0) {
$( '#product-import-sort-wrapper' ).sortable( 'cancel' );
}
}
dropdown menu is created in shadowDOM
almost perfect,
but the problem is how to hide the dropdown menu when click on any where else in window
class NavComponent extends HTMLElement {
constructor() {
super();
let template = document.currentScript.ownerDocument.querySelector('template');
let clone = document.importNode(template.content, true);
let root = this.attachShadow({ mode: 'open' });
root.appendChild(clone);
}
connectedCallback() {
let ddc = this.shadowRoot.querySelectorAll('.dropdowncontainer')
let dd = this.shadowRoot.querySelectorAll('.dropdown');
let ddc_length = ddc.length
for (let index = 0; index < ddc_length; index++) {
ddc[index].addEventListener('click', e => {
dd[index].classList.toggle('show');
});
}
/** have to update the code ............ */
window.onclick = function (event) {
} /** END - have to update the code ............ */
}
}
customElements.define('app-nav', NavComponent)
please refer this demo for complete code
The best solution, as #guitarino suggested is to define a dropdown menu custom element.
When the menu is clicked, call a (first) event handler that will show/hide the menu, and also add/remove a (second) dropdown event handler on window.
At its turn, the (second) dropdown event handler will call the first event handler only if the action is outside the custom element itself.
connectedCallback()
{
//mousedown anywhere
this.mouse_down = ev => !this.contains( ev.target ) && toggle_menu()
//toggle menu and window listener
var toggle_menu = () =>
{
if ( this.classList.toggle( 'show' ) )
window.addEventListener( 'mousedown', this.mouse_down )
else
window.removeEventListener( 'mousedown', this.mouse_down )
}
//click on menu
this.addEventListener( 'click', toggle_menu )
}
It works with or without Shadow DOM:
customElements.define( 'drop-menu', class extends HTMLElement
{
constructor ()
{
super()
this.attachShadow( { mode: 'open'} )
.innerHTML = '<slot></slot>'
}
connectedCallback()
{
//mousedown anywhere
this.mouse_down = ev => !this.contains( ev.target ) && toggle_menu()
//toggle menu and window listener
var toggle_menu = () =>
{
if ( this.classList.toggle( 'show' ) )
window.addEventListener( 'mousedown', this.mouse_down )
else
window.removeEventListener( 'mousedown', this.mouse_down )
}
//click on menu
this.addEventListener( 'click', toggle_menu )
}
disconnectedCallback ()
{
this.removeEventListener( 'mousedown', this.mouse_down )
}
} )
drop-menu {
position: relative ;
cursor: pointer ;
display: inline-block ;
}
drop-menu > output {
border: 1px solid #ccc ;
padding: 2px 5px ;
}
drop-menu > ul {
box-sizing: content-box ;
position: absolute ;
top: 2px ; left: 5px ;
width: 200px;
list-style: none ;
border: 1px solid #ccc ;
padding: 0 ;
opacity: 0 ;
transition: all 0.2s ease-in-out ;
background: white ;
visibility: hidden ;
z-index: 2 ;
}
drop-menu.show > ul {
opacity: 1 ;
visibility: visible ;
}
drop-menu > ul > li {
overflow: hidden ;
transition: font 0.2s ease-in-out ;
padding: 2px 5px ;
background-color: #e7e7e7;
}
drop-menu:hover {
cursor: pointer;
background-color: #f2f2f2;
}
drop-menu ul li:hover {
background-color: #e0e0e0;
}
drop-menu ul li span {
float: right;
color: #f9f9f9;
background-color: #f03861;
padding: 2px 5px;
border-radius: 3px;
text-align: center;
font-size: .8rem;
}
drop-menu ul li:hover span {
background-color: #ee204e;
}
<drop-menu><output>Services</output>
<ul>
<li>Graphic desing</li>
<li>web design</li>
<li>app design</li>
<li>theme</li>
</ul>
</drop-menu>
<drop-menu><output>tutorial</output>
<ul>
<li>css <span>12 available</span></li>
<li>php <span>10 available</span></li>
<li>javascript <span>40 available</span></li>
<li>html <span>20 available</span></li>
</ul>
</drop-menu>
An unrelated suggestion: you should probably separate .dropdown into its own <app-nav-dropdown> component and assign the 'click' event listeners in its 'constructor' or 'connectedCallback'.
The best idea for your problem is to
ddc[index].addEventListener('click', e => {
if(dd[index].classList.contains('show')) {
dd[index].classList.remove('show');
window.removeEventListener('click', handleDropdownUnfocus, true);
}
else {
dd[index].classList.add('show');
window.addEventListener('click', handleDropdownUnfocus, true);
}
});
Note: I use addEventListener with true, such that the event happens at capture, so that it's will not happen immediately after the .dropdown click handler.
And your handleDropdownUnfocus will look like
function handleDropdownUnfocus(e) {
Array.from(document.querySelectorAll('app-nav')).forEach(function(appNav) {
Array.from(appNav.shadowRoot.querySelectorAll('.dropdown')).forEach(function(dd) {
dd.classList.remove('show');
});
});
window.removeEventListener('click', handleDropdownUnfocus, true);
}
There's a problem with this solution though, that if you click on the menu item again after opening it, it will call both .dropdown and window handlers, and the net result will be that the dropdown will remain open. To fix that, you would normally add a check in handleDropdownUnfocus:
if(e.target.closest('.dropdown')) return;
However, it will not work. Even when you click on .dropdown, your e.target will be app-nav element, due to Shadow DOM. Which makes it more difficult to do the toggling. I don't know how you'd address this issue, maybe you can come up with something fancy, or stop using Shadow DOM.
Also, your code has some red flags... For example, you use let keyword in your for-loop, which is fine. The support for let is very limited still, so you're more likely going to transpile. The transpiler will just change every let to var. However, if you use var in your loop, assigning the handlers in a loop like that will not work anymore, because index for every handler will refer to the last dropdown (because index will now be global within the function's context and not local for every loop instance).
I'm a very-very beginner in jQuery and JavaScript. Basically I'm about to create custom responsive menu with 2 navigation on the right and the left side, the first one is working fine, but not with the other one. The script i took from (http://tympanus.net/codrops/2013/04/19/responsive-multi-level-menu/) . My script is look like this (codepen.io/donlego/full/xGjzWR).
HTML
<body>
<div id="dl-menu" class="dl-menuwrapper"><!-- Codrops top bar -->
<button class="dl-trigger">Open Menu</button>
<ul class="dl-menu">
<li>
Fashion
<ul class="dl-submenu">
<li>
Men
</li>
<li>
Women
<ul class="dl-submenu">
<li>Jackets</li>
<li>Knits</li>
<li>Jeans</li>
<li>Dresses</li>
<li>Blouses</li>
<li>T-Shirts</li>
<li>Underwear</li>
</ul>
</li>
<li>
Children
<ul class="dl-submenu">
<li>Boys</li>
<li>Girls</li>
</ul>
</li>
</ul>
</li>
<li>
Electronics
<ul class="dl-submenu">
<li>Camera & Photo</li>
<li>TV & Home Cinema</li>
<li>Phones</li>
<li>PC & Video Games</li>
</ul>
</li>
<li>
Furniture
<ul class="dl-submenu">
<li>
Living Room
<ul class="dl-submenu">
<li>Sofas & Loveseats</li>
<li>Coffee & Accent Tables</li>
<li>Chairs & Recliners</li>
<li>Bookshelves</li>
</ul>
</li>
<li>
Bedroom
<ul class="dl-submenu">
<li>
Beds
<ul class="dl-submenu">
<li>Upholstered Beds</li>
<li>Divans</li>
<li>Metal Beds</li>
<li>Storage Beds</li>
<li>Wooden Beds</li>
<li>Children's Beds</li>
</ul>
</li>
<li>Bedroom Sets</li>
<li>Chests & Dressers</li>
</ul>
</li>
<li>Home Office</li>
<li>Dining & Bar</li>
<li>Patio</li>
</ul>
</li>
<li>
Jewelry & Watches
<ul class="dl-submenu">
<li>Fine Jewelry</li>
<li>Fashion Jewelry</li>
<li>Watches</li>
<li>
Wedding Jewelry
<ul class="dl-submenu">
<li>Engagement Rings</li>
<li>Bridal Sets</li>
<li>Women's Wedding Bands</li>
<li>Men's Wedding Bands</li>
</ul>
</li>
</ul>
</li>
</ul>
</div><!-- /dl-menuwrapper -->
<div class="clearfix"></div>
<div id="dl-menu" class="dl-menuwrapper"><!-- Codrops top bar -->
<button class="dl-trigger">Open Menu</button>
<ul class="dl-menu">
<li>
Fashion
<ul class="dl-submenu">
<li>
Men
<ul class="dl-submenu">
<li>Shirts</li>
<li>Jackets</li>
<li>Chinos & Trousers</li>
<li>Jeans</li>
<li>T-Shirts</li>
<li>Underwear</li>
</ul>
</li>
<li>
Women
<ul class="dl-submenu">
<li>Jackets</li>
<li>Knits</li>
<li>Jeans</li>
<li>Dresses</li>
<li>Blouses</li>
<li>T-Shirts</li>
<li>Underwear</li>
</ul>
</li>
<li>
Children
<ul class="dl-submenu">
<li>Boys</li>
<li>Girls</li>
</ul>
</li>
</ul>
</li>
<li>
Electronics
<ul class="dl-submenu">
<li>Camera & Photo</li>
<li>TV & Home Cinema</li>
<li>Phones</li>
<li>PC & Video Games</li>
</ul>
</li>
<li>
Furniture
<ul class="dl-submenu">
<li>
Living Room
<ul class="dl-submenu">
<li>Sofas & Loveseats</li>
<li>Coffee & Accent Tables</li>
<li>Chairs & Recliners</li>
<li>Bookshelves</li>
</ul>
</li>
<li>
Bedroom
<ul class="dl-submenu">
<li>
Beds
<ul class="dl-submenu">
<li>Upholstered Beds</li>
<li>Divans</li>
<li>Metal Beds</li>
<li>Storage Beds</li>
<li>Wooden Beds</li>
<li>Children's Beds</li>
</ul>
</li>
<li>Bedroom Sets</li>
<li>Chests & Dressers</li>
</ul>
</li>
<li>Home Office</li>
<li>Dining & Bar</li>
<li>Patio</li>
</ul>
</li>
<li>
Jewelry & Watches
<ul class="dl-submenu">
<li>Fine Jewelry</li>
<li>Fashion Jewelry</li>
<li>Watches</li>
<li>
Wedding Jewelry
<ul class="dl-submenu">
<li>Engagement Rings</li>
<li>Bridal Sets</li>
<li>Women's Wedding Bands</li>
<li>Men's Wedding Bands</li>
</ul>
</li>
</ul>
</li>
</ul>
</div><!-- /dl-menuwrapper -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
$( '#dl-menu' ).dlmenu();
});
</script>
</body>
CSS
#font-face {
font-family: 'icomoon';
src:url('../fonts/icomoon.eot');
src:url('../fonts/icomoon.eot?#iefix') format('embedded-opentype'),
url('../fonts/icomoon.woff') format('woff'),
url('../fonts/icomoon.ttf') format('truetype'),
url('../fonts/icomoon.svg#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
.clearfix:before,
.clearfix:after {
content: " ";
display: table;
}
.clearfix:after {
clear: both;
}
/* Common styles of menus */
.dl-menuwrapper {
width: 0;
max-width: 90%;
float: left;
position: relative;
-webkit-perspective: 1000px;
perspective: 1000px;
-webkit-perspective-origin: 50% 200%;
perspective-origin: 50% 200%;
}
.dl-menuwrapper:first-child {
margin-right: 0;
}
.dl-menuwrapper button {
background: #395066;
border: none;
width: 48px;
height: 90px;
text-indent: -900em;
overflow: hidden;
position: relative;
cursor: pointer;
outline: none;
-webkit-box-shadow:inset 0 -2px rgba(255,255,255,0.1),inset 0 -3px rgba(0,0,0,0.1),0 1px rgba(0,0,0,0.05);
-moz-box-shadow:inset 0 -2px rgba(255,255,255,0.1),inset 0 -3px rgba(0,0,0,0.1),0 1px rgba(0,0,0,0.05);
box-shadow:inset 0 -2px rgba(255,255,255,0.1),inset 0 -3px rgba(0,0,0,0.1),0 1px rgba(0,0,0,0.05);
}
.dl-menuwrapper button:hover,
.dl-menuwrapper button.dl-active,
.dl-menuwrapper ul {
background: #32475b;
}
.dl-menuwrapper button:after {
content: '';
position: absolute;
width: 68%;
height: 5px;
background: #fff;
top: 30px;
left: 16%;
box-shadow:
0 10px 0 #fff,
0 20px 0 #fff;
}
.dl-menuwrapper ul {
padding: 0;
list-style: none;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
background-color: #009ee8;
width: 350px;
}
.dl-menuwrapper li {
position: relative;
width: 350px;
background-color: #395066;
}
.dl-menuwrapper li a {
display: block;
position: relative;
padding: 15px 20px;
font-size: 16px;
line-height: 20px;
font-weight: 300;
color: #fff;
outline: none;
background-color: #395066;
}
.no-touch .dl-menuwrapper li a:hover {
background: rgba(0,0,0,0.2);
}
.dl-menuwrapper li.dl-back > a {
padding-left: 30px;
background: #163a5b;
}
.dl-menuwrapper li.dl-back:after,
.dl-menuwrapper li > a:not(:only-child):after {
position: absolute;
top: 0;
line-height: 50px;
font-family: 'icomoon';
speak: none;
-webkit-font-smoothing: antialiased;
content: "\e000";
color: #eeeeee;
}
.dl-menuwrapper li.dl-back:after {
left: 10px;
color: rgba(212,204,198,0.3);
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
color: #eeeeee;
}
.dl-menuwrapper li > a:after {
right: 10px;
color: rgba(0,0,0,0.15);
}
.dl-menuwrapper .dl-menu { /* biyang kerok 2*/
position: absolute;
width: 100%;
opacity: 0;
pointer-events: none;
-webkit-transform: translateY(10px);
transform: translateY(10px);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.dl-menuwrapper .dl-menu.dl-menu-toggle {
transition: all 0.3s ease;
}
.dl-menuwrapper .dl-menu.dl-menuopen {
opacity: 1;
pointer-events: auto;
-webkit-transform: translateY(0px);
transform: translateY(0px);
}
/* Hide the inner submenus */
.dl-menuwrapper li .dl-submenu {
display: none;
}
/*
When a submenu is openend, we will hide all li siblings.
For that we give a class to the parent menu called "dl-subview".
We also hide the submenu link.
The opened submenu will get the class "dl-subviewopen".
All this is done for any sub-level being entered.
*/
.dl-menu.dl-subview li,
.dl-menu.dl-subview li.dl-subviewopen > a,
.dl-menu.dl-subview li.dl-subview > a {
display: none;
}
.dl-menu.dl-subview li.dl-subview,
.dl-menu.dl-subview li.dl-subview .dl-submenu,
.dl-menu.dl-subview li.dl-subviewopen,
.dl-menu.dl-subview li.dl-subviewopen > .dl-submenu,
.dl-menu.dl-subview li.dl-subviewopen > .dl-submenu > li {
display: block;
}
Jquery
;( function( $, window, undefined ) {
'use strict';
// global
var Modernizr = window.Modernizr, $body = $( 'body' );
$.DLMenu = function( options, element ) {
this.$el = $( element );
this._init( options );
};
// the options
$.DLMenu.defaults = {
// classes for the animation effects
animationClasses : { classin : 'dl-animate-in-1', classout : 'dl-animate-out-1' },
// callback: click a link that has a sub menu
// el is the link element (li); name is the level name
onLevelClick : function( el, name ) { return false; },
// callback: click a link that does not have a sub menu
// el is the link element (li); ev is the event obj
onLinkClick : function( el, ev ) { return false; }
};
$.DLMenu.prototype = {
_init : function( options ) {
// options
this.options = $.extend( true, {}, $.DLMenu.defaults, options );
// cache some elements and initialize some variables
this._config();
var animEndEventNames = {
'WebkitAnimation' : 'webkitAnimationEnd',
'OAnimation' : 'oAnimationEnd',
'msAnimation' : 'MSAnimationEnd',
'animation' : 'animationend'
},
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
};
// animation end event name
this.animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ] + '.dlmenu';
// transition end event name
this.transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ] + '.dlmenu',
// support for css animations and css transitions
this.supportAnimations = Modernizr.cssanimations,
this.supportTransitions = Modernizr.csstransitions;
this._initEvents();
},
_config : function() {
this.open = false;
this.$trigger = this.$el.children( '.dl-trigger' );
this.$menu = this.$el.children( 'ul.dl-menu' );
this.$menuitems = this.$menu.find( 'li:not(.dl-back)' );
this.$el.find( 'ul.dl-submenu' ).prepend( '<li class="dl-back">back</li>' );
this.$back = this.$menu.find( 'li.dl-back' );
},
_initEvents : function() {
var self = this;
this.$trigger.on( 'click.dlmenu', function() {
if( self.open ) {
self._closeMenu();
}
else {
self._openMenu();
}
return false;
} );
this.$menuitems.on( 'click.dlmenu', function( event ) {
event.stopPropagation();
var $item = $(this),
$submenu = $item.children( 'ul.dl-submenu' );
if( $submenu.length > 0 ) {
var $flyin = $submenu.clone().css( 'opacity', 0 ).insertAfter( self.$menu ),
onAnimationEndFn = function() {
self.$menu.off( self.animEndEventName ).removeClass( self.options.animationClasses.classout ).addClass( 'dl-subview' );
$item.addClass( 'dl-subviewopen' ).parents( '.dl-subviewopen:first' ).removeClass( 'dl-subviewopen' ).addClass( 'dl-subview' );
$flyin.remove();
};
setTimeout( function() {
$flyin.addClass( self.options.animationClasses.classin );
self.$menu.addClass( self.options.animationClasses.classout );
if( self.supportAnimations ) {
self.$menu.on( self.animEndEventName, onAnimationEndFn );
}
else {
onAnimationEndFn.call();
}
self.options.onLevelClick( $item, $item.children( 'a:first' ).text() );
} );
return false;
}
else {
self.options.onLinkClick( $item, event );
}
} );
this.$back.on( 'click.dlmenu', function( event ) {
var $this = $( this ),
$submenu = $this.parents( 'ul.dl-submenu:first' ),
$item = $submenu.parent(),
$flyin = $submenu.clone().insertAfter( self.$menu );
var onAnimationEndFn = function() {
self.$menu.off( self.animEndEventName ).removeClass( self.options.animationClasses.classin );
$flyin.remove();
};
setTimeout( function() {
$flyin.addClass( self.options.animationClasses.classout );
self.$menu.addClass( self.options.animationClasses.classin );
if( self.supportAnimations ) {
self.$menu.on( self.animEndEventName, onAnimationEndFn );
}
else {
onAnimationEndFn.call();
}
$item.removeClass( 'dl-subviewopen' );
var $subview = $this.parents( '.dl-subview:first' );
if( $subview.is( 'li' ) ) {
$subview.addClass( 'dl-subviewopen' );
}
$subview.removeClass( 'dl-subview' );
} );
return false;
} );
},
closeMenu : function() {
if( this.open ) {
this._closeMenu();
}
},
_closeMenu : function() {
var self = this,
onTransitionEndFn = function() {
self.$menu.off( self.transEndEventName );
self._resetMenu();
};
this.$menu.removeClass( 'dl-menuopen' );
this.$menu.addClass( 'dl-menu-toggle' );
this.$trigger.removeClass( 'dl-active' );
if( this.supportTransitions ) {
this.$menu.on( this.transEndEventName, onTransitionEndFn );
}
else {
onTransitionEndFn.call();
}
this.open = false;
},
openMenu : function() {
if( !this.open ) {
this._openMenu();
}
},
_openMenu : function() {
var self = this;
// clicking somewhere else makes the menu close
$body.off( 'click' ).on( 'click.dlmenu', function() {
self._closeMenu() ;
} );
this.$menu.addClass( 'dl-menuopen dl-menu-toggle' ).on( this.transEndEventName, function() {
$( this ).removeClass( 'dl-menu-toggle' );
} );
this.$trigger.addClass( 'dl-active' );
this.open = true;
},
// resets the menu to its original state (first level of options)
_resetMenu : function() {
this.$menu.removeClass( 'dl-subview' );
this.$menuitems.removeClass( 'dl-subview dl-subviewopen' );
}
};
var logError = function( message ) {
if ( window.console ) {
window.console.error( message );
}
};
$.fn.dlmenu = function( options ) {
if ( typeof options === 'string' ) {
var args = Array.prototype.slice.call( arguments, 1 );
this.each(function() {
var instance = $.data( this, 'dlmenu' );
if ( !instance ) {
logError( "cannot call methods on dlmenu prior to initialization; " +
"attempted to call method '" + options + "'" );
return;
}
if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {
logError( "no such method '" + options + "' for dlmenu instance" );
return;
}
instance[ options ].apply( instance, args );
});
}
else {
this.each(function() {
var instance = $.data( this, 'dlmenu' );
if ( instance ) {
instance._init();
}
else {
instance = $.data( this, 'dlmenu', new $.DLMenu( options, this ) );
}
});
}
return this;
};
$.fn.DLMenu=function(){
return this.each( function(){
if ( $( this ).data( 'dlmenu' ) ){
return false;
}
$( this ).data( 'dlmenu', true );
// plugin code
});
};
} )( jQuery, window );
Two elements on the same page cannot share the same ID.
Change $( '#dl-menu' ).dlmenu(); to $( '.dl-menuwrapper' ).dlmenu(); and it will most likely work.
Reference: https://softwareengineering.stackexchange.com/questions/127178/two-html-elements-with-same-id-attribute-how-bad-is-it-really