Vertical menu dropdown that stays open on sub menu pages - javascript

I've been having trouble trying to wrap my head around how to get my menu sub items to stay open when on the active page. I've seen similar issues with resolutions but I can't seem to get them to work when I implement them, so I do apologise for the question being asked before. I'm using Jquery to open the sub menu items, allowing more than one to be open at any time and also closed at any time, the only issue is when a sub menu item is clicked the menu collapses and i'd like it to stay open when that page is visited. I've attached a JSFiddle to this so you can visualise what my questions is. Thank you, all help is greatly appreciated!
$(document).ready(function(e) {
$('.has-sub').click(function() {
$(this).toggleClass('open');
});
});
body {
font-family: sans-serif;
}
.left-nav {
width: 250px;
/*change to 100% of contain*/
background-color: gray;
}
.left-nav ul {
padding: 0px;
}
.left-nav li {
list-style: none;
}
.left-nav a {
display: block;
padding: 10px 0px 10px 20px;
text-decoration: none;
color: #fff;
}
/*hiding sub menu items*/
.left-nav ul ul {
display: none;
}
/*giving jquery a target*/
.left-nav ul li.open ul {
display: block;
}
/*arrow*/
.left-nav .has-sub:before {
content: '\203A';
float: right;
color: #fff;
margin-top: 10px;
margin-right: 40px;
/*to make it move*/
transform: rotate(90deg);
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
}
.left-nav li.open:before {
content: '\2039';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav class="left-nav">
<ul>
<li class="has-sub">1st
<!-- First nest -->
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
<li>Sub 3</li>
<li>Sub 4</li>
</ul>
</li>
<li class="has-sub">2nd
<!-- First nest -->
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
<li>Sub 3</li>
<li>Sub 4</li>
</ul>
</li>
<li>3rd</li>
<li>4th</li>
<li>5th</li>
</ul>
</nav>

So what you need to do is to stop the propagation of the event when the user clicks the sub menu item using stopPropagation
$(document).ready(function(e) {
$('.has-sub').click(function() {
$(this).toggleClass('open');
});
// Just add this
$('.has-sub li a').click(function (e) {
e.stopPropagation();
});
});
body {
font-family: sans-serif;
}
.left-nav {
width: 250px;
/*change to 100% of contain*/
background-color: gray;
}
.left-nav ul {
padding: 0px;
}
.left-nav li {
list-style: none;
}
.left-nav a {
display: block;
padding: 10px 0px 10px 20px;
text-decoration: none;
color: #fff;
}
/*hiding sub menu items*/
.left-nav ul ul {
display: none;
}
/*giving jquery a target*/
.left-nav ul li.open ul {
display: block;
}
/*arrow*/
.left-nav .has-sub:before {
content: '\203A';
float: right;
color: #fff;
margin-top: 10px;
margin-right: 40px;
/*to make it move*/
transform: rotate(90deg);
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
}
.left-nav li.open:before {
content: '\2039';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav class="left-nav">
<ul>
<li class="has-sub">1st
<!-- First nest -->
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
<li>Sub 3</li>
<li>Sub 4</li>
</ul>
</li>
<li class="has-sub">2nd
<!-- First nest -->
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
<li>Sub 3</li>
<li>Sub 4</li>
</ul>
</li>
<li>3rd</li>
<li>4th</li>
<li>5th</li>
</ul>
</nav>

Use e.target and the methods is() and hasClass() to check the element you are clicking:
$(document).ready(function() {
$('.has-sub').click(function(e) {
var val = $(e.target).parent();
if (val.is("li") && val.hasClass("has-sub")) {
$(val).toggleClass('open');
}
})
})
body {
font-family: sans-serif;
}
.left-nav {
width: 250px;
/*change to 100% of contain*/
background-color: gray;
}
.left-nav ul {
padding: 0px;
}
.left-nav li {
list-style: none;
}
.left-nav a {
display: block;
padding: 10px 0px 10px 20px;
text-decoration: none;
color: #fff;
}
/*hiding sub menu items*/
.left-nav ul ul {
display: none;
}
/*giving jquery a target*/
.left-nav ul li.open ul {
display: block;
}
/*arrow*/
.left-nav .has-sub:before {
content: '\203A';
float: right;
color: #fff;
margin-top: 10px;
margin-right: 40px;
/*to make it move*/
transform: rotate(90deg);
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
}
.left-nav li.open:before {
content: '\2039';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav class="left-nav">
<ul>
<li class="has-sub">
1st
<!-- First nest -->
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
<li>Sub 3</li>
<li>Sub 4</li>
</ul>
</li>
<li class="has-sub">2nd
<!-- First nest -->
<ul>
<li>Sub 1</li>
<li>Sub 2</li>
<li>Sub 3</li>
<li>Sub 4</li>
</ul>
</li>
<li>3rd</li>
<li>4th</li>
<li>5th</li>
</ul>
</nav>

Related

Change on hover effect to click to showdropdown list

Ok, I have a dropdown list which appears 'on hover', is there a way to change it to have it appear 'on click' instead. Ideally, CSS only, but open to JS options as well. If I could push the boat out further I would also like a cross in the top right-hand corner to close the dropdown list.
I have made a fiddle here so that you can see my current 'on hover' setup
current CSS
ul {
padding: 15px;
list-style: none;
text-align: center;
}
.navigationWrap ul li {
width: 100%;
float: left;
color: #000;
font-size: 16px;
position: relative;
}
.navigationWrap ul li a {
text-decoration: none;
color: #000;
display: block;
}
.navigationWrap ul li a:hover {
color: #000;
background-color: #e6ffe6;
}
.navigationWrap ul li ul.subNav {
position: absolute;
width: 95%;
padding: 10px;
background-color: #fff;
border: #4399fc solid 1px;
display: none;
z-index: 999;
left: 0;
top: 100%;
text-align: left;
max-height: 350px;
overflow: auto;
overflow-x: hidden;
}
.navigationWrap ul li ul.subNav li {
float: left;
width: 100%;
font-size: 20px;
letter-spacing: 1px;
padding-bottom: 10px;
}
.navigationWrap ul li ul.subNav a {
float: left;
width: 100%;
transition: all 0.3s ease-in-out;
display: block;
}
.navigationWrap ul li ul.subNav li a:hover {
color: #000;
padding-left: 10px;
}
.navigationWrap ul li ul.subNav li.active a {
color: #04A000;
}
.navigationWrap ul li.dropdown:hover ul.subNav {
display: block;
}
The HTML code
<div class="navigationWrap">
<ul>
<li class="dropdown">☰ See Options<ul class="subNav">
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
<li>Option 4</li>
<li>Option 5</li>
<li>
<p></p>
</li>
<li>Option 6</li>
<li>Option 7</li>
</ul>
</li>
</ul>
</div>
Many thanks, Jason.
It's very much possible to using JavaScript. I did by jQuery. I closed dropdown if user click outside of the dropdown container.
Here is JS code.
$(function(){
$('.dropdown .dropdown-toggle').on('click', function(e){
e.preventDefault;
e.stopPropagation;
$(this).parents('.dropdown').toggleClass('show');
});
// Remove dropdown if click outside of dropdown
const $menu = $('.dropdown');
$(document).mouseup(e => {
if (!$menu.is(e.target) // if the target of the click isn't the container...
&& $menu.has(e.target).length === 0) // ... nor a descendant of the container
{
$menu.removeClass('show');
}
});
});
CSS change.
/* Before */
.navigationWrap ul li.dropdown:hover ul.subNav {
display: block;
}
/* After */
.navigationWrap ul li.dropdown.show ul.subNav {
display: block;
}
Here is HTML.
<div class="navigationWrap">
<ul>
<li class="dropdown">
<a class="dropdown-toggle" href="javascript:void(0);">☰ See Options</a>
<ul class="subNav">
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
<li>Option 4</li>
<li>Option 5</li>
<li>
<p></p>
</li>
<li>Option 6</li>
<li>Option 7</li>
</ul>
</li>
</ul>
</div>
Here is the CodePen.
You can do this
We toggle class (add/remove) and change display value via css
document.querySelector('.dropdown').onclick = () => {
document.querySelector('.dropdown').classList.toggle('show');
};
If class .show exists then display: block
.navigationWrap ul li.show ul.subNav{
display: block;
}
Check this out

From drop-list lists show only the selected list and its subcategories

I want to show only the entire dropdown list, but when you select an option, the remaining list is removed and remains only selected is displayed. I'll show you the current code, all I want is when I click on "Main Item One", the "Main Item Two" option disappears and show only "Main Item One" with options. Or when I click on "Main Item Two" show only "Main Item Two" with subcategory.
var allHasChildren = document.querySelectorAll(".item-has-children a");
for (var x = 0; x < allHasChildren.length; x++) {
allHasChildren[x].onclick = function() {
// get the first submenu and toggle using classes
var subMenu = this.parentNode.getElementsByClassName("sub-menu")[0];
if (subMenu.classList.contains('selected')) {
subMenu.classList.remove("selected");
} else {
subMenu.classList.add("selected");
}
}
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown .dropbtn {
background-color: blue;
color: #fff;
font-size: 17px;
font-weight: 600;
border: none;
cursor: pointer;
height: 55px;
background: #153161;
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
padding: 12px 50px;
}
.dropdown .dropbtn i {
margin-left: 30px;
color: #8391ab;
}
.dropdown .dropbtn .arrow {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #8191aa;
margin: 100%;
padding-top: 4px;
}
.dropdown .dropbtn-two {
background: red;
}
.dropdown .dropbtn-three {
background: yellow;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
width: 330px;
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 25px;
text-decoration: none;
display: flex;
justify-content: flex-start;
}
.dropdown-content a:hover {
background-color: #F8F8F8;
}
.dropdown:hover .dropdown-content {
display: block;
background: white;
opacity: 0.8;
width: 100%;
}
.sub-menu {
display: none;
}
.sub-menu.selected {
display: block;
transition: transform 0.6s;
}
ul {
list-style: none;
}
<div class="dropdown">
<button class="dropbtn dropbtn-one">
DropDown
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<ul>
<li class="item-has-children">
Main Item One
<ul class="sub-menu">
<li>Sub Item One</li>
<li>Sub Item Two</li>
<li>Sub Item Three</li>
<li>Sub Item Four</li>
<li>Sub Item Five</li>
<li>Sub Item Six</li>
</ul>
</li>
<div class="hr2"></div>
<li class="item-has-children">
Main Item Two
<ul class="sub-menu">
<li>Sub Item One</li>
<li>Sub Item Two</li>
<li>Sub Item Three</li>
<li>Sub Item Four</li>
<li>Sub Item Five</li>
<li>Sub Item Six</li>
</ul>
</li>
<div class="hr2"></div>
<li class="item-has-children">
Main Item Two
<ul class="sub-menu">
<li>Sub Item One</li>
<li>Sub Item Two</li>
<li>Sub Item Three</li>
<li>Sub Item Four</li>
<li>Sub Item Five</li>
<li>Sub Item Six</li>
</ul>
</li>
</ul>
</div>
</div>
Inside the click event handler you can get a reference to the parent li element of the selected option using
e.target.parentNode
Now if you loop over the complete list of options
document.querySelectorAll(".item-has-children")
and compare it to the reference you can hide the remaining options.
Here's some code:
var clicked = false;
var allHasChildren = document.querySelectorAll(".item-has-children a");
for (var x = 0; x < allHasChildren.length; x++) {
allHasChildren[x].onclick = function(e) {
var subMenu = this.parentNode.getElementsByClassName("sub-menu")[0];
if (subMenu.classList.contains('selected')) {
subMenu.classList.remove("selected");
} else {
subMenu.classList.add("selected");
}
var allOptions = document.querySelectorAll(".item-has-children");
if (!clicked) {
clicked = true;
var currentOption = e.target.parentNode;
for (var a = 0; a < allOptions.length; a++) {
if (allOptions[a] != currentOption) {
allOptions[a].style.display = "none";
}
}
} else {
clicked = false;
for (var a = 0; a < allOptions.length; a++) {
allOptions[a].style.display = "block";
}
}
}
}
document.getElementsByClassName("dropdown")[0].onmouseout = function() {
if (clicked && window.getComputedStyle(document.getElementsByClassName("dropdown-content")[0], null).getPropertyValue("display") == "none") {
var allOptions = document.querySelectorAll(".item-has-children");
for (var a = 0; a < allOptions.length; a++) {
allOptions[a].style.display = "block";
}
var subMenu = document.getElementsByClassName("sub-menu");
for (var a = 0; a < subMenu.length; a++) {
subMenu[a].classList.remove("selected");
}
clicked = false;
}
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown .dropbtn {
background-color: blue;
color: #fff;
font-size: 17px;
font-weight: 600;
border: none;
cursor: pointer;
height: 55px;
background: #153161;
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
padding: 12px 50px;
}
.dropdown .dropbtn i {
margin-left: 30px;
color: #8391ab;
}
.dropdown .dropbtn .arrow {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #8191aa;
margin: 100%;
padding-top: 4px;
}
.dropdown .dropbtn-two {
background: red;
}
.dropdown .dropbtn-three {
background: yellow;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
width: 330px;
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 25px;
text-decoration: none;
display: flex;
justify-content: flex-start;
}
.dropdown-content a:hover {
background-color: #F8F8F8;
}
.dropdown:hover .dropdown-content {
display: block;
background: white;
opacity: 0.8;
width: 100%;
}
.sub-menu {
display: none;
}
.sub-menu.selected {
display: block;
transition: transform 0.6s;
}
ul {
list-style: none;
}
<div class="dropdown">
<button class="dropbtn dropbtn-one">
DropDown
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<ul>
<li class="item-has-children">
Main Item One
<ul class="sub-menu">
<li>Sub Item One</li>
<li>Sub Item Two</li>
<li>Sub Item Three</li>
<li>Sub Item Four</li>
<li>Sub Item Five</li>
<li>Sub Item Six</li>
</ul>
</li>
<div class="hr2"></div>
<li class="item-has-children">
Main Item Two
<ul class="sub-menu">
<li>Sub Item One</li>
<li>Sub Item Two</li>
<li>Sub Item Three</li>
<li>Sub Item Four</li>
<li>Sub Item Five</li>
<li>Sub Item Six</li>
</ul>
</li>
<div class="hr2"></div>
<li class="item-has-children">
Main Item Two
<ul class="sub-menu">
<li>Sub Item One</li>
<li>Sub Item Two</li>
<li>Sub Item Three</li>
<li>Sub Item Four</li>
<li>Sub Item Five</li>
<li>Sub Item Six</li>
</ul>
</li>
</ul>
</div>
</div>

How can I expand the dropdown list wider than the title

I am trying to make a simple dropdown menu using Javascript that slides up and down when the user hovers over the title.
It all works OK as long as the dropdown items are no wider than the title. But I cannot work out how to accommodate wider dropdown items, other than to hard code the width of all the items in the relevant list.
Is there a better way to do this (my code is below).
$(document).ready(function() {
$(document).click(function(event) {
var text = $(event.target).text();
});
$("nav li").hover(
function() {
$(this)
.find("ul>li")
.stop()
.slideDown(400);
},
function() {
$(this)
.find("ul>li")
.stop()
.slideUp(400);
}
);
});
ul {
left: 0;
margin: 0;
padding: 0; /* to prevent the menu indenting - ul has padding by default */
list-style: none;
}
ul li {
float: left;
height: 30px;
line-height: 30px;
text-align: center;
background-color: purple;
width: 100px;
}
ul li a {
color: #fff;
text-decoration: none;
}
ul li li {
background-color: purple;
color: #fff;
text-decoration: none;
display: none;
}
ul li li:hover {
background-color: green;
}
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<div>
<nav>
<ul>
<li>Home
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Extra Extra Wide Link 3</li>
<li>Link 4</li>
</ul>
</li>
<li>About Us</li>
<li>Contact</li>
<li>FAQ
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
</ul>
</li>
<li>Help</li>
</ul>
</nav>
</div>
In you css where ul li li add width
ul li li {
background-color: purple;
color: #fff;
text-decoration: none;
display: none;
width: 200px;
}
$(document).ready(function() {
$(document).click(function(event) {
var text = $(event.target).text();
});
$('nav li').hover (
function() {
$(this).find('ul>li').stop().slideDown(400);
},
function() {
$(this).find('ul>li').stop().slideUp(400);
}
);
});
ul {
left: 0;
margin: 0;
padding: 0; /* to prevent the menu indenting - ul has padding by default */
list-style: none;
}
ul li {
float: left;
height: 30px;
line-height: 30px;
text-align: center;
background-color: purple;
width: 100px;
}
ul li a {
color: #fff;
text-decoration: none;
}
ul li li {
background-color: purple;
color: #fff;
text-decoration: none;
display: none;
width: 200px
}
ul li li:hover {
background-color: green;
}
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<div>
<nav>
<ul>
<li>Home
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Extra Extra Wide Link 3</li>
<li>Link 4</li>
</ul>
</li>
<li>About Us</li>
<li>Contact</li>
<li>FAQ
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
</ul>
</li>
<li>Help</li>
</ul>
</nav>
</div>

submenu collapse instead of fly over dropdown

I am trying to add the functionality of a collapse to my submenu, but no dice yet. I have looked into couple solutions and nothing worked so far.
The issue is that on desktop the menu has a functionality (it opens to the side) and on mobile is suppose to act like a drop down.
Here is some images to help me convey what I am trying to do on mobile:
The first menu is behaving correctly. I want to be like this flyover like dropdown
The Second menu however (green one), I wanted to open like a collapse, showing its sub menus, but still showing the blue menu remaining items below
**Like this **
Here is a fiddle, not sure if you will be able to see the mobile option. Any help would be appreciated.
$('ul.first-menu>li').click(function(event) {
var li = $(this);
var liOld = $('ul.active').parents("li");
var el = (event.target || event.srcElement);
$(el).find('span').toggleClass('arrow-down arrow-up');
if($('ul.active').length!=0){
$('ul.active').removeClass('active').slideUp('fast', function(){
if(li.index() != liOld.index()){
li.children('ul.submenu').slideDown(600).addClass('active');
}
});
}
else{
$(this).children('ul.submenu').slideDown(600).addClass('active');
}
});
if ( $(window).width() < 736) {
var i;
var $acc = $('.has-children');
$acc.click(function(event){
event.stopPropagation();
event.preventDefault();
var el = (event.target || event.srcElement);
console.log('clicked');
$(this).find('ul.submenu').toggle();
$(el).find('span').toggleClass('arrow-down arrow-up');
});
}
.bg-nav {
width: 796px;
display: flex;
background-color: #323232;
}
.bg-nav ul {
background-color: #323232;
padding: 0;
display: flex;
align-items: stretch;
flex-direction: column;
margin-bottom: 0;
width: 80%;
}
.bg-nav ul li {
list-style-type: none;
text-align: center;
border-bottom: .5px solid #ccc;
}
.bg-nav ul li a {
padding: .8rem 1rem;
text-decoration: none;
display: block;
color: #fff;
font-family: 'ArialMT', 'Arial';
font-weight: 400;
font-size: 13px;
}
.bg-nav ul li:hover {
background-color: #2c3e50;
}
.bg-nav ul li a:hover {
color: #fff;
}
.has-children ul, .has-children ul .has-children ul{
display: none;
width: 100%;
position:absolute;
background-color: #fff;
}
.has-children ul li a {
color: #00A2CD;
}
.bg-nav .submenu {
border: 1px solid #ccc;
}
.bg-nav .submenu li {
text-align: left;
}
.bg-nav .submenu li:hover {
background-color: #966ea2;
}
.bg-nav .first-submenu li a:hover,
.bg-nav .first-submenu li a:active,
.bg-nav .first-submenu li a:focus {
color: #fff;
}
.bg-nav .arrow {
font-family: FontAwesome;
float: right;
}
.bg-nav .arrow-down::after {
content: "\f107";
font-size: 16px;
}
.bg-nav .arrow-up::after {
content: "\f106";
font-size: 16px;
}
.bg-nav .arrow-right::after {
content: "\f105";
font-size: 16px;
}
#media only screen
and (min-device-width : 320px)
and (max-device-width : 568px) {
.bg-nav {
justify-content: flex-start;
width: 100%;
margin-top: 35px;
}
.bg-nav ul {
width: 100%;
}
.bg-nav ul li {
text-align: left;
border-bottom: .5px solid #ccc;
}
.bg-nav .has-children ul li a {
padding-left: 25px;
}
.bg-nav .has-children ul .has-children ul {
display: none;
}
.bg-nav .first-submenu {
width: 90%;
margin-left: 5%;
}
}
#media only screen and (min-device-width : 736px) {
.bg-nav {
align-items: stretch;
justify-content: center;
height: 36px;
}
.bg-nav .has-children ul li a {
padding-left: 15px;
}
.bg-nav ul {
flex-direction: row;
justify-content: center;
}
.bg-nav ul li {
position: relative;
flex: 1 0;
border-left: .5px solid #ccc;
}
.bg-nav ul li:last-of-type {
border-right: .5px solid #ccc;
}
.bg-nav .submenu li,
.bg-nav .submenu li:last-of-type {
border-left: none;
border-right: none;
}
.bg-nav .has-children ul .has-children ul{
left:100%;
top: 0;
}
.bg-nav .has-children ul {
display:flex;
flex-direction:column;
}
.bg-nav .has-children ul .has-children:hover ul{
display:flex;
flex-direction:column;
}
.bg-nav .submenu .arrow-down::after {
content: "\f105";
font-size: 16px;
}
}
.bg-nav .has-children ul,
.submenu, .first-submenu {
display: none;
}
.arrow {
pointer-events: none;
}
.has-children ul li {
background-color: #f5f5f5;
}
.has-children ul {
display: none;
}
.active {
display: block;
}
.has-children ul li {
background-color: lightblue;
}
.has-children ul li .second-submenu li {
background-color: lightgreen;
}
/** {
outline: 1px solid orange;
}*/
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="bg-nav">
<ul class="first-menu">
<li class="has-children">
<a href="#">Page 1
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu first-submenu">
<li>Menu 1</li>
<li class="has-children">
<a href="#">Menu 2
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu second-submenu">
<li>Sub Menu 1</li>
<li>Sub Menu 2</li>
<li>Sub Menu 3</li>
<li>Sub Menu 4</li>
<li>Sub Menu 5</li>
<li>Sub Menu 6</li>
<li>Sub Menu 7</li>
</ul>
</li>
<li class="has-children">
<a href="#">Menu3
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu second-submenu">
<li>Sub Menu 1</li>
<li>Sub Menu 2</li>
<li>Sub Menu 3</li>
<li>Sub Menu 4</li>
<li>Sub Menu 5</li>
<li>Sub Menu 6</li>
<li>Sub Menu 7</li>
</ul>
</li>
<li class="has-children">
<a href="#">Menu 4
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu second-submenu">
<li>Sub Menu 1</li>
<li>Sub Menu 2</li>
<li>Sub Menu 3</li>
<li>Sub Menu 4</li>
<li>Sub Menu 5</li>
<li>Sub Menu 6</li>
<li>Sub Menu 7</li>
</ul>
</li>
<li class="has-children">
<a href="#">Menu 5
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu second-submenu">
<li>Sub Menu 1</li>
<li>Sub Menu 2</li>
<li>Sub Menu 3</li>
<li>Sub Menu 4</li>
<li>Sub Menu 5</li>
<li>Sub Menu 6</li>
<li>Sub Menu 7</li>
</ul>
</li>
<li class="has-children">
<a href="#">Menu 6
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu second-submenu">
<li>Sub Menu 1</li>
<li>Sub Menu 2</li>
<li>Sub Menu 3</li>
<li>Sub Menu 4</li>
<li>Sub Menu 5</li>
<li>Sub Menu 6</li>
<li>Sub Menu 7</li>
</ul>
</li>
<li class="has-children">
<a href="#">Menu 7
<span class="arrow arrow-down"></span>
</a>
</li>
<li class="has-children">
<a href="#">Menu 8
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu second-submenu">
<li>Sub Menu 1</li>
<li>Sub Menu 2</li>
<li>Sub Menu 3</li>
<li>Sub Menu 4</li>
<li>Sub Menu 5</li>
<li>Sub Menu 6</li>
<li>Sub Menu 7</li>
</ul>
</li>
</ul>
</li>
<li class="has-children">
<a href="#">Page 2
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu first-submenu">
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
<li>Menu 4</li>
<li>Menu 5</li>
<li>Menu 6</li>
<li>Menu 7</li>
</ul>
</li>
<li class="has-children">
<a href="#">Page 3
<span class="arrow arrow-down"></span>
</a>
<ul class="submenu first-submenu" value="hide/show">
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
<li>Menu 4</li>
<li>Menu 5</li>
</ul>
</li>
<li>Page 4</li>
</ul>
</div>

Most efficient way to create a slide-down submenu in jQuery

I am trying to make a submenu which slides down from the main menu bar when hovering over a certain element. I am currently doing this using the following code:
$(document).ready( function() {
$('.navlist li a').hover( function() {
if( $(this).attr( 'data-param' ) == "parent" )
{
$('#subnavbar-' + $(this).attr( 'data-slug' )).slideDown( 200 );
}
}, function() {
if( $(this).attr( 'data-param' ) == "parent" )
{
var name = '#subnavbar-' + $(this).attr( 'data-slug' );
setTimeout( function() {
if( !$(name).is(':hover') )
{
$(name).slideUp( 200 );
}
}, 200 );
}
});
});
a {
color: white;
}
.navbar {
background-color: green;
margin-bottom: 0;
height: 30px;
}
ul.navlist {
list-style: none;
text-indent: 0;
margin: 0;
padding: 0;
float: left;
}
ul.navlist li {
display: block;
width: 100px;
float: left;
}
.subnavbar {
background-color: blue;
margin-top: 0;
height: 20px;
display: none;
}
ul.subnavlist {
list-style: none;
text-indent: 0;
margin: 0;
padding: 0;
float: left;
}
ul.subnavlist li {
display: block;
width: 80px;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="navbar">
<ul class="navlist">
<li>Item 1</li>
<li>Hover Here</li>
<li>Item 3</li>
</ul>
</div>
<div class="subnavbar" id="subnavbar-test">
<ul class="subnavlist">
<li>Subitem 1</li>
<li>Subitem 2</li>
<li>Subitem 3</li>
</ul>
</div>
As you can see by running the snippet; it works, but there are lots of bugs that I'm not sure what the best way to iron out are. Firstly, if the user hovers back and forth over the main menu item I don't want the event to be spammed, I could solve this problem using a setTimeout() and clearTimeout() but I'd like a better way if at all possible. Secondly, I'm not sure how best to get the subnavbar not to retract if the user has hovered over it instead of the parent menu item, how I'm doing it at the moment works, but then if the user hovers off, the navbar doesn't retract.
The efficient solution would be using just CSS. Absolutely no JQUERY required! Try this Fiddle.
HTML:
<nav>
<ul>
<li>Item 1</li>
<li>Item 2
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3
<ul>
<li>Sub Sub Item 1</li>
<li>Sub Sub Item 2</li>
</ul>
</li>
</ul>
</li>
<li>Item 3
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
</ul>
</li>
</ul>
</nav>
CSS Code:
nav {
margin: 100px auto;
text-align: center;
}
nav ul ul {
display: none;
}
nav ul li:hover > ul {
display: block;
}
nav ul {
background: -moz-linear-gradient(center top , #efefef 0%, #bbbbbb 100%) repeat scroll 0 0 rgba(0, 0, 0, 0);
border-radius: 10px;
box-shadow: 0 0 9px rgba(0, 0, 0, 0.15);
display: inline-table;
list-style: outside none none;
padding: 0 10px;
position: relative;
}
nav ul::after {
clear: both;
content: "";
display: block;
}
nav ul li {
float: left;
}
nav ul li:hover {
background: -moz-linear-gradient(center top , #4f5964 0%, #5f6975 40%) repeat scroll 0 0 rgba(0, 0, 0, 0);
}
nav ul li:hover a {
color: #fff;
}
nav ul li a {
color: #757575;
display: block;
padding: 15px 20px;
text-decoration: none;
}
nav ul ul {
background: none repeat scroll 0 0 #5f6975;
border-radius: 0;
padding: 0;
position: absolute;
top: 100%;
}
nav ul ul li {
border-bottom: 1px solid #575f6a;
border-top: 1px solid #6b727c;
float: none;
position: relative;
}
nav ul ul li a {
color: #fff;
padding: 5px 10px;
}
nav ul ul li a:hover {
background: none repeat scroll 0 0 #4b545f;
}
nav ul ul ul {
left: 100%;
position: absolute;
top: 0;
}
as you told me better css I've made a quick fiddle for you.
Of course you need to polish the style but the "working" is there.
css just:
.container {
height:30px;
background-color:green;
color:#fff;
}
.container > ul {position:relative;}
.container > ul li {
display:inline-block;
margin-right:30px;
}
.container > ul >li > ul {
position:absolute;
left:0;
background-color:blue;
top:0px;
z-index:-1;
}
.container > ul > li:hover > ul {
top:30px;
-webkit-transition: all 0.4s ease;
-moz-transition: all 0.4s ease;
-ms-transition: all 0.4s ease;
-o-transition: all 0.4s ease;
transition: all 0.4s ease;
}
edited: I change the aproach and insteed of makign the transition with height I've use just top. check it out
to solve your first issue I would recommend using the jQuery plugin Hover Intent. Like the name suggests, this provides an easy way to determine if the user intends to hover over the element and avoids the possibility of spamming the animation by quickly hovering in and out of the element multiple times.
To solve your second issue, if possible, you can add a containing element around both navbar and subnavbar and use that to close the subnavbar when you leave the containing element, if the subnavbar happens to be visible.
HTML:
<div id="containing_element">
<div class="navbar">
<ul class="navlist">
<li>Item 1</li>
<li>Hover Here</li>
<li>Item 3</li>
</ul>
</div>
<div class="subnavbar" id="subnavbar-test">
<ul class="subnavlist">
<li>Subitem 1</li>
<li>Subitem 2</li>
<li>Subitem 3</li>
</ul>
</div>
</div>
jQuery:
$(document).ready( function() {
function hoverEnter() {
$('#subnavbar-test').slideDown(200);
}
$('#hoverlink').hoverIntent( hoverEnter );
$('#containing_element').mouseleave(function () {
if($('#subnavbar-test').is(':visible')) {
$('#subnavbar-test').slideUp(200);
}
});
});
Remember to include the script for hover intent as well.

Categories