I am using a Bootstrap dropdown as a vertical menu coming from a button clicked on the navbar. I added some jQuery to allow the dropdown to slide down smoothly rather than pop into existence, but now I've run into the problem of the dropdown closing when anywhere (inside or outside) is clicked.
Some of the "links" in the dropdown menu are actually accordion style headers that show a list of pages when clicked, so I need the dropdown to stay open when there is a click within.
My main problem is that I have found a few solutions that work, but they seem to overwrite the smooth slide functionality of the dropdown.
This is the code I'm using for the slide down:
$('.dropdown').on('show.bs.dropdown', function(e){
$(this).find('.dropdown-menu').first().stop(true, true).slideDown();
});
$('.dropdown').on('hide.bs.dropdown', function(e){
$(this).find('.dropdown-menu').first().stop(true, true).slideUp();
});
and this is the general structure of my menu:
<div class="dropdown">
<button type="button" class="dropdown-toggle nav-tog" id="dropdownMenu1" data-toggle="dropdown">
<span class="sr-only">Toggle navigation</span>
<i class="fa fa-bars" style="color: #A05317; font-size: 30px;"></i>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1" id="mainMenu">
<ul id="main-nav">
<li class="nav-item">NAV HEADER (accordion)
<ul>
<li>Sub Nav 1</li>
<li>Sub Nav 2</li>
<li>Sub Nav 3</li>
<li>Sub Nav 4</li>
</ul>
</li>
<li class="nav-item">NAV HEADER (accordion)
<ul>
<li>Sub Nav 1</li>
<li>Sub Nav 2</li>
<li>Sub Nav 3</li>
<li>Sub Nav 4</li>
</ul>
</li>
<li class="nav-item">NAV HEADER</li>
<li class="nav-item">NAV HEADER</li>
<li class="nav-item">NAV HEADER</li>
<li class="nav-item">NAV HEADER</li>
<li class="nav-item">NAV HEADER (accordion)
<ul>
<li>Sub Nav 1</li>
<li>Sub Nav 2</li>
<li>Sub Nav 3</li>
<li>Sub Nav 4</li>
</ul>
</li>
</ul>
</ul>
</div>
Any help would be greatly appreciated!
Related
I have been trying to get this to work for a while with no success.
I have a multi-level navigation and I would like to align all child UL's with is main parent/grandparent UL.
right now the structure is something like this:
<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>
so, I have it setup so the second submenu shows on hover, I was able to get the second-submenu size to its grandparent UL with this:
$('.first-menu').each(function(){
$(this).find('ul.second-submenu')
.css('min-height', $(this).outerHeight(true));
});
and its position is set to absolute, top: 0 , and left: 100%, display: inline-block ...
but I would like to align all the second-submenu uls with the grandparent ul up top.
right now I have:
But, I would like to get something like this:
Appreciate the help.
Here your main problem is the CSS part where the relative parent you are using to position the submenu, you don't need the JS part and you can get your result if you just avoid the relative be on the li and just ensure you have it on the main ul, check this snippet:
.first-menu {
position: relative;
height: 300px;
width: 150px;
background: orange;
}
.submenu {
display: none;
position: absolute;
left: 100%;
top: 0;
width: 100%;
height: 100%;
background: orange;
}
.has-children:hover>.submenu {
display: block;
}
li:hover {
background:green;
}
ul {
padding: 0;
list-style: none
}
li {
line-height: 30px;
padding: 5px;
background: tomato
}
a {
text-decoration: none;
color: white
}
<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>
</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>
</ul>
</li>
</ul>
Note:
Here is a snippet with relative position on the li element so you can see how this works wrong like your actual screenshots:
.first-menu {
position: relative;
height: 300px;
width: 150px;
background: orange;
}
.submenu {
display: none;
position: absolute;
left: 100%;
top: 0;
width: 100%;
height: 100%;
background: orange;
}
.has-children:hover>.submenu {
display: block;
}
li:hover {
background: green;
}
ul {
padding: 0;
list-style: none
}
li {
position: relative;
line-height: 30px;
padding: 5px;
background: tomato
}
a {
text-decoration: none;
color: white
}
<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>
</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>
</ul>
</li>
</ul>
In my html page, there is a list of all the categories. Each category has some subcategories. I want that as a list item is clicked, its subcategories are toggled i.e. if they are visible, they should be hidden and vice versa. Also at one time, only one list item should be able to show its subcategories. I have tried this answer, but it doesn't help. I won't mind in using either JS, JQuery etc or changing the syntax altogether.
My work till now is
<ul>
<li class="outer-list-element">
category 1
<ul class="inner-list" id="inner-list">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
<li class ="outer-list-element">
category 3
<ul class="inner-list" id="inner-list">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
css
li {
margin-bottom: 8px;
}
.inner-list {
margin-top: 10px;
display: none;
}
Script
$('.outer-list-element').click(function () {
$(this).children().toggle();
})
$(".outer-list").click(function() {
$(this).siblings('.inner-list').toggle();
return false;
});
Feel free to update this fiddle
https://jsfiddle.net/7vmtd2px/1/
Please try following code.
$('.outer-list').click(function () {
//Hide other ul
$('ul .inner-list').not($(this).parent('li').find('ul')).hide();
//Toggle clicked ul
$(this).parent('li').find('ul').toggle();
});
li {
margin-bottom: 8px;
}
.inner-list {
margin-top: 10px;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="outer-list-element">
category 1
<ul class="inner-list">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
<li class="outer-list-element">
category 2
<ul class="inner-list">
<li>sub category 1</li>
<li> sub category 2</li>
</ul>
</li>
<li class ="outer-list-element">
category 3
<ul class="inner-list">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
</ul>
You can create a class(showInnerList in this case) with display:block property. So on click of element you can add this class to the DOM which you want to show.
Also I am using addClass & removeClass instead of toggle. The reason is on click of any DOM,I intend to find the element which has showInnerClass and remove this same class from it & then add this same class to the sibling
HTML
<ul class="myList">
<li class="outer-list-element">
category 1
<ul class="inner-list" id="inner-list">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
<li class="outer-list-element">
category 2
<ul class="inner-list" id="inner-list_1">
<li>sub category 1</li>
<li> sub category 2</li>
</ul>
</li>
<li class="outer-list-element">
category 3
<ul class="inner-list" id="inner-list_2">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
</ul>
JS
$('.outer-list-element a').click(function() {
$('.myList').find('ul.showInnerList').removeClass('showInnerList');
$(this).next('ul.inner-list').addClass('showInnerList');
})
Also note that you are using same id for ul class="inner-list". id need to be unique
JSFIDDLE
try With this..
$(document).on('click', ".outer-list", function() {
$('.inner-list').hide(); //first hide all the element having class inner-list
//$(this).siblings('.inner-list').toggle();
$(this).next('ul').toggle(); // here is the trick
return false;
});
and HERE is the FIDDLE
Thanks Mohit
Here's the same thing as Sarath's answer, but with the additional functionality of automatically collapsing the present list if it's clicked twice.
<ul>
<li class="outer-list-element">
category 1
<ul class="inner-list" id="inner-list">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
<li class="outer-list-element">
category 2
<ul class="inner-list" id="inner-list">
<li>sub category 1</li>
<li> sub category 2</li>
</ul>
</li>
<li class ="outer-list-element">
category 3
<ul class="inner-list" id="inner-list">
<li>sub category 1</li>
<li>sub category 2</li>
</ul>
</li>
</ul>
CSS:
li {
margin-bottom: 8px;
}
.inner-list {
margin-top: 10px;
display: none;
}
jQuery:
$(document).on('click', ".outer-list", function()
{
$(this).parents().siblings().children(".inner-list").hide();
$(this).siblings('.inner-list').toggle();
});
Here's the jsFiddle in case you want to see it in action.
I am trying to integrate the accordion feature to wordpress menu.
i came across with a code but it didn't give me the full expectation(not really a js guru).
here is the html code:
<div class="off-canvas-wrap" data-offcanvas>
<div class="inner-wrap">
<nav class="tab-bar">
<section class="left-small">
<a class="left-off-canvas-toggle menu-icon" href="#"><span></span></a>
</section>
</nav>
<aside class="left-off-canvas-menu">
<ul class="off-canvas-list">
<li><label>Foundation</label></li>
<li>Option 1</li>
<li>Option 2 <span class="right"> + </span></li>
<ul class="off-canvas-submenu">
<li>Sub menu 1</li>
<li>Sub menu 2</li>
<li>Sub menu 3</li>
</ul>
<li>Option 3</li>
<li>Option 4 <span class="right"> + </span></li>
<ul class="off-canvas-submenu">
<li>Sub menu 1</li>
<li>Sub menu 2</li>
<li>Sub menu 3</li>
</ul>
<li>Option 5</li>
<li>Option 6</li>
</ul>
</aside>
<section class="main-section">
Content
</section>
<a class="exit-off-canvas"></a>
</div>
</div>
and here is the jscript:
$(document).foundation();
$(".off-canvas-submenu").hide();
$(".off-canvas-submenu-call").click(function() {
var icon = $(this).parent().next(".off-canvas-submenu").is(':visible') ? '+' : '-';
$(this).parent().next(".off-canvas-submenu").slideToggle('fast');
$(this).find("span").text(icon);
});
this works perfectly only for a static page, also work a bit in wordpress but the stuggle is that when you click on item in the menu all the li with submenu opens instead of one at a time. take attention of the html structure of the menu:
<aside class="left-off-canvas-menu">
<ul class="off-canvas-list">
<li><label>Foundation</label></li>
<li>Option 1</li>
<li>Option 2 <span class="right"> + </span></li>
<ul class="off-canvas-submenu">
<li>Sub menu 1</li>
<li>Sub menu 2</li>
<li>Sub menu 3</li>
</ul>
<li>Option 3</li>
<li>Option 4 <span class="right"> + </span></li>
<ul class="off-canvas-submenu">
<li>Sub menu 1</li>
<li>Sub menu 2</li>
<li>Sub menu 3</li>
</ul>
<li>Option 5</li>
<li>Option 6</li>
</ul>
</aside>
the ul submenu is in the same level with the li menu.
in wordpress the ul.submenu is inside the parent li menu.
<aside class="left-off-canvas-menu">
<ul class="off-canvas-list">
<li><label>Foundation</label></li>
<li>Option 1</li>
<li>
Option 2 <span class="right"> + </span>
<ul class="off-canvas-submenu">
<li>Sub menu 1</li>
<li>Sub menu 2</li>
<li>Sub menu 3</li>
</ul>
</li>
</ul>
</aside>
the curent code is actually working a bit, but we don't want to open all the submemnu by only clicking a single li.
here is the codepen
$(document).foundation();
$(".off-canvas-submenu").hide();
$(".off-canvas-submenu-call").click(function() {
var icon = $(this).parent().find(".off-canvas-submenu").is(':visible') ? '+' : '-';
$(this).parent().find(".off-canvas-submenu").slideToggle('fast');
$(this).find("span").text(icon);
});
Made it worked this way using .find(), you will need to style the .off-canvas-submenu since its not on the same level as the other elements anymore therefor not inheriting the style of first level <ul> but in all you should me good.
Edit:
Works with multiple submenu
<aside class="left-off-canvas-menu">
<ul class="off-canvas-list">
<li><label>Foundation</label></li>
<li>Option 1</li>
<li>
Option 2 <span class="right"> + </span>
<ul class="off-canvas-submenu">
<li>Sub menu 1</li>
<li>Sub menu 2</li>
<li>Sub menu 3</li>
</ul>
</li>
<li>
Option 3 <span class="right"> + </span>
<ul class="off-canvas-submenu">
<li>Sub menu 1</li>
<li>Sub menu 2</li>
<li>Sub menu 3</li>
</ul>
</li>
</ul>
</aside>
Here's a question on how to center a <select> scroll bar to the selected item.
I'd like to find a way to do the same thing for a Bootstrap Dropdown Menu so that the menu automatically scrolls to the .active list item.
Here's some code:
<ul>
<li class="dropdown switch">
<a href="active" class="dropdown-toggle" data-toggle="dropdown" id="switch-a">
<span>Name</span>
</a>
<ul class="dropdown-menu switch-items" id="switch-dd">
<li>...</li>
<li>...</li>
<li class="active active-dd"></li>
.
.
.
<li>...</li>
</ul>
</li>
</ul>
Is this possible or do I have to a HTML <select> and spending time styling it?
Not only is it possible, it's actually much easier to deal with native HTML objects than it is to deal with the mysteries and inconsistencies of styling the <select> element.
You just have to listen to the dropdown open event with shown.bs.dropdown.
Once it's open, just find the only .active item and call .focus() to bring it into view.
Like This:
$(".dropdown").on("shown.bs.dropdown", function() {
$(this).find(".dropdown-menu li.active a").focus()
});
Demo in Stack Snippets:
$(".dropdown.focus-active").on("shown.bs.dropdown", function() {
$(this).find(".dropdown-menu li.active a").focus()
});
.scrollable-menu {
height: auto;
max-height: 200px;
overflow-x: hidden;
}
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/css/bootstrap.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/js/bootstrap.js"></script>
<div class="container" >
<div class="dropdown focus-active">
<a href="#" class="btn btn-primary"
data-target="#" data-toggle="dropdown"
aria-haspopup="true" role="button" aria-expanded="false">
Dropdown trigger
<span class="caret"></span>
</a>
<ul class="dropdown-menu scrollable-menu" role="menu" aria-labelledby="dLabel">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li class="active">Item 9</li>
<li>Item 10</li>
<li>Item 11</li>
<li>Item 12</li>
</ul>
</div>
</div>
Event handler without jquery:
const activeOptionCollection = document.getElementsByClassName('active');
if (activeOptionCollection.length > 0) {
const [activeOption] = activeOptionCollection;
activeOption.scrollIntoView();
}
I`ve got a big list with the width of li 33%, so there are 3 columns.
computers monitors hi-fi
sex-toys pancakes scissors
In each of them there is UL hidden, which on click slideToggle.
JQuery
$('.subCategory > .parentleaf:has(ul) > .categoryicon').click(function() {
$(this).siblings('ul').slideToggle('fast');
});
The problem that dont slide as I want, they rebuld the list every time, like so
computers monitors hi-fi
[li]cars sex-toys pancakes
[li]dogs scissors
I want to slide them this way:
computers monitors hi-fi
[li]cars pancakes scissors
[li]dogs
sex-toys
How can I achieve this??
jsFiddle
jsFiddle 2 - in this case need the red bg color be for 3 columns
I added a clearer div between every three li
<ul class="subCategory">
<li class="parentleaf">
<span class="categoryicon">click</span>
<span class="categoryname">Cars</span>
<ul class="submenu">
<li>Car 1</li>
<li>Car 2</li>
<li>Car 3</li>
<li>Car 4</li>
</ul>
</li>
<li class="parentleaf">
<span class="categoryicon">click</span>
<span class="categoryname">Phones</span>
<ul class="submenu">
<li>Phone 1</li>
<li>Phone 2</li>
<li>Phone 3</li>
<li>Phone 4</li>
</ul>
</li>
<li class="parentleaf">
<span class="categoryicon">click</span>
<span class="categoryname">Guns</span>
<ul class="submenu">
<li>Gun 1</li>
<li>Gun 2</li>
<li>Gun 3</li>
<li>Gun 4</li>
</ul>
</li>
<div class="clearer"></div>
<li class="parentleaf">
<span class="categoryicon">click</span>
<span class="categoryname">Notebooks</span>
<ul class="submenu">
<li>Notebook 1</li>
<li>Notebook 2</li>
<li>Notebook 3</li>
<li>Notebook 4</li>
</ul>
</li>
CSS
.clearer
{
clear:both;
}
You can also get the index of the current clicked li and with some math add a clearer div on click and remove it when needed but there I think there is no need for this