jQuery dropdown toggle find active li and keep the dropdown open - javascript

I've got some trouble with a custom dropdown toggle.
The menu looks like this in HTML and I do not have the option to edit the code for that so I have to solve it with css/jQuery:
<ul>
<li>Link"</li> <-- when this is active it has class="active"
<li>Link"</li>
<li>Link"</li>
<li class=""> <-- this is always empty
<span>Title</span>
<ul>
<li class="active">Link</li> <-- this is currently active
<li>Link</li>
<li>Link</li>
</ul>
</li>
</ul>
I'm a bit afraid to ask but here is what I've got:
$('.menu li[class=""] span').click(function() {
const self = $(this).parent();
self.toggleClass("menu-open");
});
if($('.menu li[class=""]').find('li.active').length !== 0){
$(this).addClass("menu-open");
}
The first function which actually works pretty well checks if there is a li with a class="" as this indicates it has a submenu. Clicking the span in the li toggles the class menu-open which results in either having the menu open or collapsed.
Now I would like to keep the menu open when I'm on a page that's in the submenu. If that's the case the li in the sub ul is active, the parent li is not.
Unfortunately that doesn't work. I also tried different approaches such as hasClass, children but none of it worked either. I pinpointed the issue mostly to:
$(this).addClass("menu-open");
as it seems that $(this) was never found. If I change $(this) to $('body') for example and add a hide(), I clearly see that it works because I get a blank page :)
Any advise on what the issue is would be much appreciated as I nearly gave up now.
$(function() {
$('.menu li[class=""] span').click(function() {
const self = $(this).parent();
self.toggleClass("menu-open");
});
if($('.menu li[class=""]').find('li.active').length !== 0){
$(this).addClass("menu-open");
}
});
.menu{width:200px}
ul{list-style-type:none}
li{background:#ccc;margin:2px}
span{display:block}
.menu li[class=""]{
height:20px;
overflow:hidden;
cursor:pointer;
}
.menu-open{
height:auto;
overflow:visible;
cursor:pointer;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
</head>
<body>
<div class="menu">
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
<li class="">
<span>Title</span>
<ul>
<li class="active">Link</li>
<li>Link</li>
<li>Link</li>
</ul>
</li>
<li>Link<li>
<li>Link<li>
<li class="">
<span>Title</span>
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
</ul>
</li>
</ul>
</div>
</body>
</html>

Try to use the each operator that JQuery provides.
It's something like this:
$('.menu li').each(function() {
if($(this).find('li.active').length !== 0) {
$(this).addClass('menu-open');
}
});
In your case, the context that is using the this expression doesn't represent the selector you tried to use in if.
in the example using each, the this expression that is inside the function represents the element being interacted represented by the selector.

Related

I need help hiding an element when hovering over another non child/parent/sibling element

I'm a beginner when it comes to javascript and I'm trying to write a script to hide a class when I hover over another class. I've written this piece of code however it isn't working as I'd like it to.Could someone give me some pointers as to why this code isn't working and some advice on how to get it to achieve the results I'm looking for.
$(document).ready( function () {
"use strict";
document.getElementsByClassName('nav-bar').onmouseover = function(){
document.getElementsByClassName('site-title').style.display="none";
};
document.getElementsByClassName('nav-bar').onmouseout = function(){
document.getElementsByClassName('site-title').style.display="inline";
};
});
edit
#Jonas
$(document).ready( function () {
"use strict";
document.getElementsByClassName('nav-bar').forEach(function(el){el.onmouseover = function(){
document.getElementsByClassName('site-title').forEach(function(el){el.style.display="none";}
);
};
}
);
document.getElementsByClassName('nav-bar').forEach(function(el){el.onmouseout = function(){
document.getElementsByClassName('site-title').forEach(function(el){el.style.display="inline";});
};});
});
this is your adapted code. I'm not sure why it isn't working have i done it correctly?
edit 2
<body>
<Header>
<div class="navigation-wrap">
<div class="logo"><img src="../images/logo2.jpg" alt="Lewis Banks Logo" title="Lewis Banks & Sons Ltd"></div>
<div class="navigation">
<nav class="nav-menu">
<ul class="clearfix" >
<li class="nav-button"><a class="nav-bar" href="../index.html">Home</a></li>
<li class="nav-button">
<a id="product-button">Products</a>
<ul id="product-list">
<li class="menu-dropdown2"> AC
<ul id="AC-sublist">
<li class="dropdown-content"><a href="../CSW1-Switch.html">CSW1 Switch (15mm)<a>
</li>
<li class="dropdown-content"><a href="../CSW2-Switch.html">CSW2 Switch (20mm)<a>
</li>
<li class="dropdown-content"><a href="../CSW10-Switch.html">CSW10 Switch (30mm)<a>
</li>
</ul>
</li>
<li class="menu-dropdown2"><a href="../DC-Products.html" >DC</a>
<ul id="DC-sublist">
<li class="dropdown-content"><a href="../Cartridge-Brush-Holders.html">Cartridge Brush Holders<a>
</li>
<li class="dropdown-content">Brush Holder Caps
</li>
<li class="dropdown-content">Extruded Brush Holders
</li>
<li class="dropdown-content">Pressed Brass Brush Holders</li>
<li class="dropdown-content">Aluminium Brush Rockers
</li>
<li class="dropdown-content">Pressed Brass Brush Rockers</li>
<li class="dropdown-content">Tachometer Brush Rocker
</li>
<li class="dropdown-content">Carbon Brushes
</li>
<li class="dropdown-content">Constant Force Springs
</li>
</ul>
</li>
</ul>
</li>
<li class="nav-button"><a class="nav-bar" href="../Applications.html">Applications</a></li>
<li class="nav-button"><a class="nav-bar" href="../Old-and-New.html">Old & New</a></li>
<li class="nav-button"><a class="nav-bar" href="../About-Us.html">About Us</a></li>
</ul>
</nav>
</div>
</div>
</Header>
<div class="site-title">
<h1>Lewis Banks & Sons</h1>
<h3><q>Labor Omnibus Unus</q></h3>
<h4><i>Company motto since 1916</i></h4>
</div>
</body>
This is my html code, I apologize in advanced for the confusing state it is in this is the first website i've ever tried to to make and I've had to do a lot of trial and error and other acts of desperation when i came unstuck.
I have a top menu bar which has submenu's. I managed to do that using CSS.
The problem i have is as i hover over the sub-menus they overlap with the site title which makes the page look ugly. I don't want to move the site titel down so instead i'd like to remove it whenever you hover over the initial menu buttons. I want to to do this whilst maintaining the page structre (ie there's whitespace where the tite was).
It seems you are using jQuery. Why not use jQuery selectors and methods to achieve your goal. It is easier to read and understand. Take a look at the following pages for more information:
http://api.jquery.com/on/
https://api.jquery.com/mouseover/
https://api.jquery.com/mouseout/
Try this for example:
$(document).ready( function () {
$(document).on('mouseover', '.nav-bar', function() {
jQuery('.site-title').css('display', 'none');
});
$(document).on('mouseout', '.nav-bar', function() {
jQuery('.site-title').css('display', 'inline-block');
});
});
GetElementsByClassName returns a HTMLCollection. You need to loopover, and add to each:
document.getElementsByClassName('nav-bar').forEach(function(el){el.onmouseover = function(){
document.getElementsByClassName('site-title').forEach(function(el){el.style.display="none";});
};});
Like Dais mentioned in his answer, it looks like you are using jQuery so why not use the built in jQuery mouse events. Either that or you copied your code from somewhere and didn't realize that $ is a shortcut for jQuery and for your javascript to work you need to include jQuery. To include jQuery you need a statment similar to below in your html. This will include jQuery from google's content delivery network (CDN). There are other CDN's available including ones directly from jQuery.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
https://api.jquery.com/category/events/mouse-events/
Below is a working example using jQuery events and selectors.
$(document).ready(function() {
"use strict";
$('#site-title').mouseover( function() {
$('#site-title').hide(500);
});
$('#nav-bar').mouseleave( function() {
$('#site-title').show(500);
});
});
#site-title {
position:absolute;
background:red;
width:100%;
height:50px;
}
#nav-bar {
position:absolute;
background:green;
width:100%;
height:50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav id="nav-bar">I am the nav bar
<button>Nav1</button>
<button>Nav2</button>
<button>Nav3</button>
</nav>
<header id="site-title">I am the site title</header>

Show/Hide Submenu in wordpress

I know there's tons of pages dedicated to this on SO but my piece of code just doesn't seem to work. I keep getting a "Uncaught ReferenceError: $ is not defined". I've made use of jquery to show/hide my wordpress submenu when a user hover over the parent. (See code below)
Javascript
<script type="text/javascript">
$('.menu .nav li > .sub-menu').parent().hover(function() {
var submenu = $(this).children('.sub-menu');
if ( $(submenu).is(':hidden') ) {
$(submenu).slideDown(200);
} else {
$(submenu).slideUp(200);
}
});
HTML
<div id="masthead" class="menu navbar navbar" role="banner">
<div class="logo-navbar container-logo">
<div class="container-fullwidth">
<div class="navbar-header">
<div class="menu-left-container"><ul id="menu-left" class="nav navbar-nav"><li id="menu-item-184" class="menu-item">Item 1</li>
<li id="menu-item-239" class="menu-item"> Item 2
<ul class="sub-menu">
<li id="menu-item-238" class="menu-item">Sub-Item 1</li>
<li id="menu-item-237" class="menu-item">Sub-Item 2</li>
<li id="menu-item-240" class="menu-item">Sub-Item 3</li>
<li id="menu-item-241" class="menu-item">Sub-Item 4</li>
</ul>
</li>
</ul></div><a href="#" class="navbar-brand">
<img src="logo.png">
</a>
<div class="menu-right-container"><ul id="menu-right" class="nav navbar-nav"><li id="menu-item">Item 3 Illustrations</li>
<li id="menu-item-189" class="menu-item">Item 4</li>
</ul>
</div>
</div>
</div>
</div>
I guess im not referencing the right function. Hope somebody helps me out!
Cheers!
You need to make sure:
1.- You have included jQuery on the header/footer of your page.
2.- If you are using native jQuery from WordPress is defined as jQuery not as $.
3.- You should wrap your code inside of a $(document).ready function.
Example:
<script type="text/javascript">
(function($) {
$(document).ready(function() {
$('.menu .nav li > .sub-menu').parent().hover(function() {
var submenu = $(this).children('.sub-menu');
if ( $(submenu).is(':hidden') ) {
$(submenu).slideDown(200);
} else {
$(submenu).slideUp(200);
}
});
});
})( jQuery );
</script>
Reasons:
1.- You can test this on the chrome console by typing $ or jQuery in the console, and you should have and output like:
function (a,b){return new e.fn.init(a,b,h)}
2.- If you are using the jQuery included by WordPress you need to use jQuery instead of $ as mentioned before to avoid conflicts with other libs.
3.- Since otherwise the code is executed as soon as the document is ready if not your code can be executed before the DOM elements are on the document.
Are you importing jQuery properly? If not add this to the header.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
If that isn't the issue you can also try to see if adding $ = jQuery to the top of your script does the trick.

Getting/changing class of an element with other elements in it. JavaScript/JQuery

I made a blog archive in the format of this:
+Year
+Month
Title
Sample code:
<ul>
<span class="toggle">+</span>
<li class="year">$year
<ul>
<span class="toggle">+</span>
<li class="month active">$month
<ul>
<li class="title active">$title</li>
</ul>
</li>
</ul>
</li>
</ul>
I used $(this).next().toggle(), which works fine toggling the lists, but the entire list is expanded in the beginning when the page loads, and I don't want that.
So I changed to changing class names (active/inactive). I want to change the class of the month/title lists to inactive and back when the + span is clicked. The problem is using $(this).next() doesn't work.
If I try $(this).next().hasClass("active");
It will return a false. Or console.log($(this).next().attr("class"));, which gives undefined.
$(this).next().html(); gives:
<li class="month active"><span class="toggle">+</span><ul><li class="title active">...</li></ul></li></ul></li></ul>
The very next thing that follows the + span is the list with class of active, but it doesn't recognize the class? I don't understand why .toggle() works, but this doesn't.
What option do I have to make this work?
The idea is to capture the click event on the span class and toggle active/inactive on the year so that it shows correctly. Here's some psuedo code:
$('.toggle').on('click', function(){
$(this).next().toggleClass('active').toggleClass('inactive');
});
This will only work if the element has a class of inactive on page load, like this:
<ul>
<span class="toggle">+</span>
<li class="year inactive">$year
<ul>
<span class="toggle">+</span>
<li class="month active">$month
<ul>
<li class="title active">$title</li>
</ul>
</li>
</ul>
</li>
</ul>
When you had your initial toggle working but it displayed the items on load, you could have set the next element (the unordered list) to
style="display: none"
As for
console.log($(this).next().attr("class");
You are missing a parenthesis:
console.log( $(this).next().attr("class") );
Hope this helps.
By using little bit of CSS and toggling the class of ul to active only on click will fix your issue. Below is a working example.
$('.toggle').on('click', function() {
$(this).parent().toggleClass('active');
});
ul {
list-style-type: none;
}
ul:not(#MainNode) {
display: none;
}
ul.active > li > ul {
display: block !important;
}
.toggle {
cursor: pointer
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="MainNode">
<span class="toggle">+</span>
<li class="year">Year
<ul>
<span class="toggle">+</span>
<li class="month active">Month
<ul>
<li class="title active">Title</li>
</ul>
</li>
</ul>
</li>
</ul>

how to make this slide menu work

Example: https://jsfiddle.net/lmgonzalves/xj6a74jy/1/
Result: I would like to make a slideUp + slideDown menu the has multiple levels.
I'm stuck trying to get this slide menu to work and I'm not sure how about to get it to work. I've tried using "height"0px" on some css when clicked but ultimately I get back to the same problem. I can make it through the first click in making the slide menu work (meaning there is a slideUp and slideDown), but any level after that the slider just slides up and not down leaving me with no visible menu. Here is what I have:
$('.mobile-nav .navigation a').on('click',function(e){
e.preventDefault();
var t = $(this);
var active = t.closest('li.active');
active.children('ul,a, li.back').not(t.closest('ul')).slideUp();
t.next('ul').slideDown();
});
.mobile-nav .navigation {background:#eee; width:250px; position:relative;}
.mobile-nav .navigation ul {margin:0; padding:0;}
.mobile-nav .navigation a {display:block; line-height:30px;}
.mobile-nav .navigation li ul {display:none;}
<div class="mobile-nav">
<div class="navigation">
<ul>
<li class="active">
All
<ul style="display:block;">
<li>
Topic 1
<ul>
<li class="back">Back</li>
<li>
Some Topic
<ul>
<li class="back">Back</li>
<li>
Some Topic1
((( the menu keeps getting repeated here going deeper, using the format of BackTopic 1Topic 1Topic2 with varying number of li's in each ul.
So the first ul looks like this:
<div class="mobile-nav">
<div class="navigation">
<ul>
<li class="active">
All
</li>
</ul>
/* With 3 more ul's and li's in each
<ul></ul>
<ul></ul>
<ul></ul>
</div>
</div>
When I click on one of the a href tag's, the menu slides to the next level showing the ul, which is the 2nd ul. But when I click on any of the li a's within this ul, I can see the menu start to slide down, but at the same time, the entire ul slides up showing nothing. The ul that was opened now is display:none; even though the next ul is now showing block. I can't figure out how to keep the slides going as they were in the first click.
I can redo classes and such if there is a better way to make this happen.
Fiddled something for you: Fiddle
Hope this is what you need. Just changed the way of selecting the elements.
(function ($) {
"use strict";
$('.mobile-nav')
.on('click', 'a', function (e) {
var $cTarget = $(e.currentTarget),
$dropdown = $cTarget.next('ul'),
$parentUl = $cTarget.closest('ul'),
$activeElem = $parentUl.find('ul.active');
$parentUl.children('li').each(function (key, elem) {
var $elem = $(elem);
if(!$cTarget.parent('li').is($elem)) {
$elem.slideUp();
}
});
$activeElem.toggleClass('active').slideUp();
if (!$dropdown.is($activeElem)) {
$dropdown.toggleClass('active').slideDown();
}
})
.on('click', '.back', function (e) {
var $cTarget = $(e.currentTarget),
$dropdown = $cTarget.closest('ul');
$dropdown.toggleClass('active').slideUp();
$cTarget.parents('li').first().siblings().slideDown();
});})(jQuery);
So these answers are going to be pretty close to each other, but I haven't seen one that meets your "only one item can be open at a time criteria." The JQuery is a little verbose if you want to stick with slipeUp and slideDown but here's an example of the code for handling it for the top-level unordered lists:
$('.toplevel > span').click(function () {
if ($(this).parent().hasClass('activeTop')) {
$('.activeTop').removeClass('activeTop');
$(this).parent().children('ul').slideUp();
return;
}
$('.activeTop').children('ul').slideUp();
$('.activeTop').removeClass('activeTop');
$(this).parent().addClass('activeTop');
$('.activeTop').children('ul').slideDown();
});
I replaced the a tags with spans (and cleaned up the HTML a bit) so I didn't have to deal with my demo fiddle navigating away, but here's a demo implementing the behavior for both top- and second-level menu items.
Check out this fiddle, I would make your structure a little simpler like this https://jsfiddle.net/jk90pxgt/1/ and then your jQuery is only a couple of lines. You can obviously add back buttons if you would like and styling is up to you but this is just a much cleaner way to do the slide menu. Also don't use links and prevent the default, it is just extra code. Just do your click function on the LI
Here is the jQuery
$(".mobile-menu li").click(function(e){
e.stopPropagation();
$(this).children(".sub-menu").slideToggle();
});
New HTML Structure
<ul class="mobile-menu">
<li>First Item
<ul class="sub-menu">
<li>First Sub-Item
<ul class="sub-menu">
<li>First Sub-Item</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Item
<ul class="sub-menu">
<li>First Sub-Item
<ul class="sub-menu">
<li>First Sub-Item
<ul class="sub-menu">
<li>First Sub-Item</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Second Sub-Item</li>
</ul>
</li>
<li>Third Item</li>
</ul>
And CSS
.sub-menu {
display:none;
}
li {
cursor:pointer;
}
Here is how I was able to make this work:
$('.mobile-nav .navigation a').on('click',function(e){
var t = $(this), li = t.closest('li'), ul = li.closest('ul'), a = ul.siblings('a');
if(li.hasClass('back')) {
e.preventDefault();
//do back code here
var sib = ul.closest('li').siblings('li');
a = ul.parents('ul').eq(0).siblings('a');
ul.slideUp();
sib.add(a).slideDown();
} else if(t.siblings().length > 0) {
e.preventDefault();
li.siblings('li').add(a).slideUp();
t.next('ul').slideDown();
}
});

Making parent link unclickable in a drop-down menu with jQuery

I have a drop-down menu that are dynamically added through WordPress. It looks like this:
Pictures
Sea
Forest
City
"Sea", "Forest" and "City" is categories with "Pictures" as parent category.
My question is:
How do I make the "Pictures" category unclickable?
I did this with jQuery:
$(document).ready(function(){
//Make parent links unclickable
$(".page-item-3").click(function(){
return false;
});
});
...and this with CSS:
li.page-item-3 a {
cursor:default;
}
.page-item-3 ul li a {
cursor: pointer;
}
Markup looks like this:
<div id="menu" class="jqueryslidemenu">
<ul>
<li class="cat-item cat-item-1 current_page_item">Blabla</li>
<li class="page_item page-item-2">Blabla
<ul>
<li class="page_item page-item-28">Blabla</li>
<li class="page_item page-item-30">Blabla</li>
<li class="page_item page-item-39">Blabla</li>
</ul>
</li>
<li class="page_item page-item-3">Blabla
<ul>
<li class="page_item page-item-5">Blabla 1</li>
<li class="page_item page-item-7">Blabla 2</li>
<li class="page_item page-item-9">Blabla 3</li>
<li class="page_item page-item-11">Blabla 4</li>
<li class="page_item page-item-13">Blabla 5</li>
</ul>
</li>
<li class="page_item page-item-15">Blabla
<ul>
<li class="page_item page-item-222">Blabla</li>
<li class="page_item page-item-224">Blabla</li>
<li class="page_item page-item-226">Blabla</li>
</ul>
</li>
<li class="page_item page-item-17">Blabla</li>
<li class="page_item page-item-36">Blabla</li>
</ul>
</div>
This almost works But the jQuery code makes all the drop-down links unclickable too.
It would be great if anyone knows how to remove the status bar url while hover the "Pictures" link. But I don't think that is possible to make in moderns browsers such as Safari och Firefox?
Thanks!
I don't know what control you have because of Wordpress but you're having this problem because everything is contained in the title list item (page-item-3) and you're cancelling the click on this item. If you can apply a class to the title link itself, you can apply the jQuery to that directly.
Unfortunately you can't say ".page-item-3 a" because this apply to all links in the list.
Re-Edit - This should select the first link in the list and cancel the click value of that. You may need to apply this for each 'title' link you have.
$(".page-item-3 a:first").click(function() {
return false;
}
$(".page-item-3").children("a").click(function(e) {
e.preventDefault();
});
*****or with CSS*****
.unclickable {
z-index:-1;
}
$(".page-item-3").children("a").addClass("unclickable");
just replace the href attribute value with #. That way when the user clicks on it, the page goes to #, which is the same page they are on, and nothing happens. Keep the CSS you wrote so the hand pointer does not appear when they hover it, but remove the jQuery code.
Using jQuery:
$(".page-item-3>a").attr("href", "#")
Try this:
$(document).ready(function(){
//Make parent links unclickable
$("div > ul > li > a").click(function(){
return false;
});
});
This will disable all the first links in your list without needing the class name.
I use this :
$j = jQuery.noConflict();
$j(document).ready(function(){
//Make parent links unclickable
$j("div[id='nav'] > ul > li > a ").removeAttr("href");
});

Categories