Multiple dropdown navigation - how to change the trigger button - javascript

I am struggling with one of my dropdowns.
Currently it is set up to be triggered by an i tag to drop down the sub menu.
$('nav li i').click(function() {
I want to change it to (nav li a) so it is not the icon that has to be pressed
I also have the code:
var child = $(this).index('nav ul li i');
but i am not sure what to change this to?
You can see all the code in jsfiddle.
http://jsfiddle.net/susannalarsen/VNYAx/

Since I do not see the <a> element anywhere, I have changed the <i> to <a> for demonstration purposes. You can see the example on http://jsfiddle.net/VNYAx/3/
Basically I changed
$('nav li i').click(function() {
to
$('nav li a').click(function() {
And also
var child = $(this).index('nav ul li i');
to
var child = $(this).index('nav ul li a');
Is this what you need?

Instead of using the index of the icon as a way to identify which dropdown you want to slide down, you can save a reference to that dropdown by searching for '.dropdown' within the element clicked.
$('nav li').click(function () {
var $childDropdown = $(this).find('.dropdown');
if ($childDropdown.is(':visible')) {
$('.dropdown').slideUp(300);
} else {
$('.dropdown').slideUp(300);
$childDropdown.slideDown(300);
}
});
http://jsfiddle.net/9Fk7j/2/

Just an alternative approach to this problem using .slideToggle(). If you need anything explaining please comment and I will edit the answer. I have commented the JavaScript below. I also removed the extra <div> in the markup as the nested <ul> is a perfectly good container.
Demo
HTML
<nav id="moo">
<ul>
<li>Item A
<ul>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</li>
<li>Item B
<ul>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
</ul>
</li>
</ul>
</nav>
CSS
ul {
padding: 0;
margin: 0;
}
li {
display: inline;
}
nav li {
position: relative;
}
ul ul {
display: none;
position: absolute;
border:1px solid #ccc;
padding: 10px;
}
ul ul li {
display: block;
}
JavaScript
var $allSubMenus = $('ul ul'); // handle to all submenus
$('nav li a').click(function (e) {
var $li = $(this).closest('li'); // get parent <li> of the <a> clicked on
var $subMenu = $li.find('ul'); // get our submenu
$allSubMenus.not($subMenu).slideUp(300); // hide all other submenus
$subMenu.slideToggle(300); // show our submenu
e.stopPropagation(); // stop event bubbling further
});
$(document).click(function () {
$allSubMenus.hide(); // no need to reselect, just use handle
});

Related

Show next sibling element if is previous element hovered with pure JavaScript

I have this snippet of html
ul { display:none }
test 1
test 2
test 3
<ul class="hh-ul-1 mm-listitem">
sub 1
</ul>
Test 4
<ul class="hh-ul-1 mm-listitem">
sub 2
</ul>
By default ul elements are hidden.
I'm trying to achieve when the a element is hovered to show only ul element which is the next sibling.
For example, if test 3 link has hovered, the ul element with sub 1 link needs to show up and to stay while the a and ul are hovered.
I'm not so experienced, so any help would be appreciated.
Have a look at adjacent sibling combinator
I add focus so you can keep the UL open
Also the a in the UL should be in an li. That makes it somewhat harder to make a sub-sub menu
ul { display:none; position: absolute; top:50px }
a:hover + ul { display: block }
a:focus + ul { display: block }
test 1
test 2
test 3
<ul class="hh-ul-1 mm-listitem">
<li>sub 1</li>
</ul>
Test 4
<ul class="hh-ul-1 mm-listitem">
<li>sub 2</li>
</ul>

Add class to current and previous list items

I have an unordered list used as a menu to play sections of a video. The list item (video portion) that's playing highlights when clicked by adding a class to it and removing the class from any other list item. I've been asked by the client to change this so that if you click on an item, the previous list items are selected also. Namely if you click on Vid 2, then Vid 1 and Start are given the 'selected' class as well as Vid 2. Clicking on Vid 3, would highlight Vid 3, Vid 2, Vid 1, Start and so on.
I would also like to remove the selected class from any list item that's ahead of the clicked item. For instance if Vid 4 is selected and the user clicks on Vid 2, then the selected class is removed from Vid 4 and Vid 2, Vid 1 and Start have the class selected. Sorry if any of this makes little sense and thanks in advance.
$("#select li a").click(function() {
$(this).parent().addClass('selected').siblings().removeClass('selected');
});
#video-controls ul li {
cursor: pointer;
}
#video-controls ul li a {
color: #5f6a72;
text-decoration: none;
display: block;
}
#video-controls ul li.selected,
#video-controls ul li.selected a {
color: #00aad2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<nav id="video-controls">
<ul id="select">
<li id="start">Start</li>
<li id="vid-1">Vid 1</li>
<li id="vid-2">Vid 2</li>
<li id="vid-3">Vid 3</li>
<li id="vid-4">Vid 4</li>
</ul>
</nav>
You can
1) traverse and get all previous elements.
2) use .andSelf() to add clicked object parent
3) add class selected to all elements returned above.
4) find remaining siblings other than returned element in step 2
5) remove class selected
$("#select li a").click(function() {
$(this).parent().prevAll().andSelf().addClass('selected').filter(':last').nextAll().removeClass('selected');
});
#video-controls ul li {
cursor: pointer;
}
#video-controls ul li a {
color: #5f6a72;
text-decoration: none;
display: block;
}
#video-controls ul li.selected,
#video-controls ul li.selected a {
color: #00aad2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<nav id="video-controls">
<ul id="select">
<li id="start">Start</li>
<li id="vid-1">Vid 1</li>
<li id="vid-2">Vid 2</li>
<li id="vid-3">Vid 3</li>
<li id="vid-4">Vid 4</li>
</ul>
</nav>

Jquery can't use 3 selectors

I'm writing a small script to make a mobile menu.
However I can't get my selectors to work together.
When I have $('#nav_button, .nav li, .nav li li') only #nav_button and .nav li works, however when I remove .nav li and leave $('#nav_button, .nav li li') this sub menu begins to work as well.
I have also tried using $('#nav_button, .nav li, .sub_menu li li') with the same results.
Does anyone have an idea why?
jquery:
var mobileMenu = function () {
var currentPosition = 'closed'
$('#nav_button, .nav li, .nav li li').click(function() {
if (currentPosition == 'closed') {
$('.nav').addClass('open');
currentPosition = 'open';
}
else {
$('.nav').removeClass('open');
currentPosition = 'closed';
}
});
};
html:
<div id="head">
<div class="container">
<div class="cover"></div>
<a href="#"><h1 class="logo pull-left">C<span class="logo_space">AV</span>O</h1>
</a> <!-- end logo !-->
<div id="nav_button"></div>
<ul class="nav pull-right">
<li>Menu
<ul class="sub_menu">
<li>Starters</li>
<li>Breakfast and Brunch</li>
<li>Salads</li>
<li>Sandwiches and Wraps</li>
<li>Pasta</li>
<li>Mains</li>
<li>Pizza</li>
</ul>
</li>
<li>About Us</li>
<li>Contact</li>
</ul>
</div><!-- end container !-->
</div> <!-- end header !-->
Firstly, two of your selectors match the same elements
$('#nav_button, .nav li, .nav li li')
.nav li matches any LI inside .nav, including nested LI's
.nav li li matches any LI's inside .nav that are inside another LI, i.e. nested LI's
In other words, LI elements inside LI elements, inside .nav are matched twice, but it shouldn't matter as jQuery removes duplicates, but it's uneccessary.
The real issue is that the event propagates, when you click a nested LI the event handler is called for the nested LI and the parent LI, so the click handler execute twice, and the class is first added, then removed (or vice versa) so there is no visible change.
Here's a simplified example
FIDDLE
Just doing
$('#nav_button, .nav li')
should be enough, but you have to add event.stopPropagation() to make sure the event doesn't propagate to the next parent LI when a LI is called, here's an example
FIDDLE
$('#nav_button, .nav li').on('click', function(e) {
e.stopPropagation();
// do stuff
});
As #adeneo said in nested li function will call twice unnecessarily. I think the other way to achieve this is divide click grabber in two selectors like below:
$('.nav').click(function() {
if (currentPosition == 'closed') {
alert('open');
$('.nav').addClass('open');
currentPosition = 'open';
}
else {
alert('close');
$(this).removeClass('open');
currentPosition = 'closed';
}
});
$('.nav > li > li').click(function() {
if (currentPosition == 'closed') {
alert('open');
$(this).addClass('open');
currentPosition = 'open';
}
else {
alert('close');
$('.nav').removeClass('open');
currentPosition = 'closed';
}
});
I wrote a fiddle to do what you expect. Please check and comment if that helpful.
http://jsfiddle.net/QMaster/y7d6osoq/

How to cancel a CSS style set by 'hover child' selector on event?

I am making a CSS based menu, with submenu items that pop up when the root element is hovered. the problem I have is that I want the CSS menu to close when I click an item in the list, but at that point I am still technically hovering over the top element, so I figured I had to use javascript to hide the menu. But when I set the display property, I set it forever and it overrides the hover selector of the parent node. And so the submenu doesn't show up anymore.
This must be pretty common, but I can't find any answers...
Any help much appreciated!
html:
<ul class="level1">
<li>one
<ul class="level2">
<li id="test">two</li>
<li>three</li>
</ul>
</li>
</ul>
css:
.level1 li:hover > ul {
display: inline;
}
.level2 {
display: none;
}
js:
document
.getElementById('test')
.addEventListener('click',function () {
this.parentNode.style.display = 'none';
// After this the menu doesn't open anymore
// because the style is overriden
});
Here's the jsfiddle
You can try this.
<ul class="level1">
<li class="hoverMe">one
<ul class="level2">
<li id="test">two</li>
<li>three</li>
</ul>
</li>
</ul>
.hoverMe:hover > ul {
display: inline;
}
var test = document.getElementById('test');
test.onclick = function () {
this.parentNode.parentNode.className = "";
};
var level1 = document.getElementsByClassName('level1')[0];
level1.getElementsByTagName("li")[0].onmouseover = function () {
if (this.className != "hoverMe") {
this.className = "hoverMe";
}
};

Show headers only for collections that are not empty

I have a couple of lists like this:
<ul>
<li class="list-header">Header</li>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
By some rules I hide and show <li> items so sometimes the list has visible <li> and sometimes it has no visible <li> elements at all except the one with list-header class, so <li class="list-header"> is still there. I want to hide that header if there are no <li> visible elements in it under header. Though I want the <ul> still to be visible.
How do I do that?
What you could do (demo):
$('ul').each(function() {
$ul = $(this);
$ul.find('.list-header').toggle($ul.has('li:not(.list-header):visible').length != 0);
});
Basically, what the above does is toggling the .list-header (I've wrapped it in the .each() in order to demo different lists) depending on whether the list .has() :visible li elements that are :not(.list-header).
UPDATE
Now it works. Sorry.
You could use the :visible and :not selectors to see if there are any elements present when you change the visibility. This example toggles the visibility when clicking the elements, and hides the header if there are no elements present:
$('li:not(".list-header")').click(function(){
$(this).toggle(10,function(){
var l = $(this).parent().children('li:visible:not(".list-header")').length
if (l>0) $(this).parent().children('li.list-header').show();
else $(this).parent().children('li.list-header').hide();
});
});
working example: http://jsfiddle.net/LDG4J/4/
There is no lh element in HTML. References: HTML5, HTML4.01, HTML 3.2. (You've removed the lh from the question.)
Instead, use an li with a class you style as you see fit (or if you're targeting recent-enough browses, no class required; just style li:nth-child(1) or li:first-child), and just don't hide that li (which will keep the ul visible):
<ul>
<li class='header'>Header</li>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
Update: I may have misunderstood. If you want to hide the header but keep the ul visible in some way:
A ul with no visible li elements will typically be invisible because it won't have any dimensions. You can override that with CSS, styling the ul to have a specific size (live example):
CSS:
ul.foo {
width: 5em;
height: 5em;
background-color: #eee;
border: 1px solid #aaa;
}
HTML:
<p><code>ul</code> with no visible children:</p>
<ul class='foo'>
<li style="display: none">This is hidden</li>
</ul>
<p><code>ul</code> with a visible child:</p>
<ul class='foo'>
<li>Visible child, note that it wraps</li>
</ul>
And of course you can apply that via jQuery rather than with a static CSS rule:
$("ul.foo").css({
width: "5em",
height: "5em",
backgroundColor: "#eee",
border: "1px solid #aaa"
});
...so you could do that when you're hiding all of the ul's elements, and undo it when showing at least one of them. After making a change:
var ul = $(/*...selector for the relevant list...*/);
if (ul.find('li:visible')[0]) {
// There's at least one visible `li` child
ul.css({/*...styles for when the list is not empty...*/});
}
else {
// There are no visible `li` children
ul.css({/*...styles for when the list is empty...*/});
}
...or better yet, add/remove a class.
try this:
$("ul li:not('.list-header')").each(function(index, val) {
if ($(this).text() == '') {
$(this).hide();
}
});
if (! ($('ul').has("li:visible:not('.list-header')").length)) {
$('li.list-header').hide();
}

Categories

Resources