Hide siblings when another dropdown is opened - javascript

I'm fairly new to .js and have been working on a dropdown nav menu. I've got most of it functioning, but I was asked to include a specific snippet for the menu activation.
I'd like to figure out how to make the other subnav items hide or scroll up when a different subnav is opened.
What am I doing wrong here?
<div id="nav_mob">
<div id="nav-toggle"><span></span></div>
<div class="dropdown_mob">
<ul>
<a class="dropdown_btn">
<li>Overview</li>
</a>
<div class="subnav_mob">
<ul>
<li>Introduction</li>
<li>Research</li>
<li class="padded">Planning & Preparation</li>
<li>International</li>
</ul>
</div>
<a class="dropdown_btn"><li>Profile</li></a>
<div class="subnav_mob">
<ul>
<li>My Account</li>
<li>My Cart</li>
<li>Check Out</li>
<li>Log Out</li>
</ul>
</div>
<a class="dropdown_btn"><li>Search</li></a>
<div class="subnav_mob">
<ul>
<li><div id="smallsearch"><input type="text"></div></li>
</ul>
</div>
</div>
</ul>
</div>
</div>
the snippet I was given:
var dropdown = document.getElementsByClassName('dropdown_btn');
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener('click', function() {
this.classList.toggle('active');
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display == 'block') {
dropdownContent.style.display = 'none';
} else {
dropdownContent.style.display = 'block';
}
});
}
and the fix I tried to implement:
$(document).ready(function() {
$('.dropdown_btn').on('click', function() {
var state = $('.dropdown_btn').is('.active');
if(state) {
$('.dropdown_btn').removeClass('active').next('.subnav_mob')
.slideUp();
} else {
$('.dropdown_btn').addClass('active').next('.subnav_mob').slideDown();
$.closest('.dropdown_btn').siblings('.dropdown_btn')
.find('.dropdown_mob').slideUp().end();
$.find('.dropdown_btn').not(this).removeClass('active');
}
})
})

Your first problem is that $('dropdown_button') selects every element with that same class, not just the one you clicked on. Operating on it will thus operate on every dropdown at once. You may have noticed that clicking one button causes every dropdown to open, and clicking another button causes them all to close again. This is why.
Your second problem is that $.closest is not a thing. If you press F12 and check out the console, you'll notice an error being thrown from that line, saying that '$.closest' is not a function. It's actually 'undefined', and attempting to invoke it as a function with () causes this error. This prevents any code after this point from being run, though even if you fix this that code still won't work for similar reasons. $.find is not a function, either, for example. closest and find, like next and slideup, are methods on jQuery instances, not on the global jQuery object itself.
This should work. Note that $(this) refers to the clicked element wrapped in a JQuery instance:
$(document).ready(function() {
$('.dropdown_btn').on('click', function() {
var state = $(this).is('active');
if(state) {
$(this).removeClass('active')
.next('.subnav_mob').slideUp();
} else {
$(this).addClass('active')
.next('.subnav_mob').slideDown();
$(this).siblings('.dropdown_btn').removeClass('active')
.next('.subnav_mob').slideUp();
}
})
})
I would recommend stepping through each call in this, compare it with the jQuery documentation, to really make sure you understand it. I'd also might recommend trying to do it without jQuery-- using the native DOM API like the original snippet was doing. Such an exercise might be frustrating, but valuable.

Related

Open/Close submenu when clicking the item name

I am having problems with the menu part of a wordpress site (salient theme), when i am on mobile, i open the menu with the hamburger button and have several options, some with sub menus, so the items with sub menus only open when clicking the little arrow icon to the right of the item, i am trying to get it to open also when you click on the item itself by making it so when you click the item it triggers a click on the arrow
here is the html of the menu
and here is the javascript i am doing to get it to work(only doing it for the first item with submenu here), i am new to javascript but for what i've seen i think this should work (i am using the Code Snippets
plugin for wordpress)
<?php
add_action( 'wp_footer', function () { ?>
<script>
var el = (document.querySelector('.menu-item.menu-item-type-custom.menu-
item-object-custom.menu-item-has-children.menu-item-5812 a'));
console.log(el);
var el2 =(document.querySelector('.menu-item.menu-item-type-custom.menu-
item-object-custom.menu-item-has-children.menu-item-5812 span'));
console.log(el2);
el.onclick = function()
{
$el2.click();
};
</script>
<?php } );
?>
SOLUTION:
aside from the answear by Alvaro Montoro i needed to encapsulate everything inside an eventListener with DomLoaded, here is the final code
add_action( 'wp_footer', function () { ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
var elSupervivencia = document.querySelector('#slide-out-widget-area > div >
div.inner > div > ul:nth-child(1) > li.menu-item.menu-item-type-custom.menu-
item-object-custom.menu-item-has-children.menu-item-5812 > a');
console.log(elSupervivencia);
var elSupervivenciaFlecha = document.querySelector('#slide-out-widget-area >
div > div.inner > div > ul:nth-child(1) > li.menu-item.menu-item-type-
custom.menu-item-object-custom.menu-item-has-children.menu-item-5812 .ocm-
dropdown-arrow i');
console.log(elSupervivenciaFlecha);
elSupervivenciaFlecha.onclick = function() {
console.log("Clicked on the span");
}
elSupervivencia.onclick = function(e)
{
e.preventDefault();
elSupervivenciaFlecha.click();
};
});
</script>
<?php } );
For what you seem to want, you almost have it. The only thing that seems to be missing is to prevent the default behavior when you click on the link (which could be a potential problem as pointed on the comments above, because the linked page may be innaccessible through the menu now).
With .preventDefault() you will prevent the default action for that element for that event, so you would just need to add that:
var el = (document.querySelector('.menu-item.menu-item-type-custom.menu-item-object-custom.menu-item-has-children.menu-item-5812 a'));
console.log(el);
var el2 = (document.querySelector('.menu-item.menu-item-type-custom.menu-item-object-custom.menu-item-has-children.menu-item-5812 span'));
console.log(el2);
el2.onclick = function() {
console.log("Clicked on the span");
}
el.onclick = function(e) {
e.preventDefault();
el2.click(); // removed the $
};
<ul class="menu">
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-5812">
Supervivencia
<ul class="sub-menu">
<li>Supervivencia 1</li>
<li>Supervivencia 2</li>
<li>Supervivencia 3</li>
</ul>
<span class="ocm-drowndown-arrow" style="top: 17.5px">
<i class="fa-angle-down"></i>
</span>
</li>
</ul>
As they pointed in the comments, that may not be too usable, as the linked page is no longer accessible on the menu, you may want to add some conditions (checking for window size or a variable/class that indicates that the mobile menu is active) to perform that preventDefault().
Apart from that, you may want to consider changing the selector for el and el2, as they are not specific and could match more than one element. I know you are using querySelector so only the first element that matches the selector will be returned, which should not be a problem for el but could be problematic with el2 (because the a could be a child span that would be selected over the sibling one that is the one you want.)

Sometimes on click executed twice

I'm having a very strange problem. Basically I have created a menu in wordpress that has parent, childs, subchilds and inside some posts.
I wrote a jquery script that adds a highlight class on the <li> in order to expand the <ul> which is inside the <li>. the format of the list is the following
<li class"menu-item-has-children"><a>Parent</a>
<ul>
<li class="menu-item-has-children"><a>Child</a>
<ul>
<li class="menu-item-has-children"><a>Sub Child</a>
<ul>
<li><a> post</a></li>
<li><a> post 2 </a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
So I wrote the following script to add highlight class to expand the sub child of the clicked <li>. When I'm clicking on any <li> of parent,child, sub child is working fine but when I click on the posts it removes the highlight class from the parent <ul> instead of taking me to the page of the clicked post.
I did a console log of $(this) and when click on any parent, child,sub child it shows me <ul> that has, but when I click on any posts it shows me first the <a> tag that it has followed by the parent <ul>. Does anyone know why?
Here is my jquery code
$(document).unbind("click").on('click',".sidebar-right li",function(e){
var found = false;
var tmp = String($(this).val());
if (tmp.indexOf("ul") != -1){
found = true;
}
if (($(this).hasClass("menu-item-has-children")) && (!$(this).children('ul').hasClass("highlight")))
{
$(this).children("a").addClass("highlight_icon");
$(this).children('ul').addClass("highlight");
return false;
}
else if (($(this).has("a") && ($(this).has("ul"))) && (!$(this).children('ul').hasClass("highlight")))
{
return true;
}
else if ($(this).children('ul').hasClass("highlight"))
{
$(this).children('a').removeClass("highlight_icon");
$(this).children('ul').removeClass("highlight");
return false;
}
else
{
return true;
}
});
Here is the console log data
Child Parent etc
Post click
SOLUTION: event.stopPropagation() as #mohamed-yousef said
try
$(".sidebar-right").on('click',".sidebar-right li", function(){
....});

Jquery to copy first link to second ul and then change class of first link

Short question:
http://jsfiddle.net/wF4FH/2/
What I want is for Page1 to be right above Page2 and Page10 above Page 20 before I change the classes. This should work for any number of elements.
The code provided gives an "Uncaught TypeError: Object # has no method 'append' ".
Long question:
I'm having problem finding the correct way to insert an li element based on the first link. The problem is I cant use id's on my markup so I have to "walk through" each class and check for names. I might just make this a lot more complicated than it is because my first two solutions didn't work the way I thought they would.
html
<ul class="nav">
<li class="active">
Start
</li>
<li class="has-child">
page1
<ul class="">
<li>
page2
</li>
</ul>
</li>
<li class="has-child">
page10
<ul class="">
<li>
page20
</li>
<li>
page30
</li>
</ul>
</li>
javascript
//Copy first link to child ul li
var pageLinks = $("li.has-child > a:first-child");
if (pageLinks != null) {
//var dropdownMenus = $("li.dropdown > a:first-child");
for (var i = 0; i < pageLinks.length; i++) {
for (var x = 0; x < pageLinks.length; x++) {
if (pageLinks[i].innerHTML === pageLinks[x].innerHTML) {
pageLinks[x].childNodes.append(pageLinks[i]);
}
}
}
}
//Change css classes
$("li.has-child").attr('class', 'dropdown');
$(".dropdown ul").addClass("dropdown-menu");
$(".dropdown a").attr("href", "#").addClass("dropdown-toggle").attr('data-toggle', 'dropdown');
strong text
What I want is for Page1 to be right above Page2 and Page10 above Page 20 before I change the classes. This should work for any number of elements.
When they are copied to the inner ul I change the top level menu item to a different class to work as a clickable dropdown men item.
The code provided gives an "Uncaught TypeError: Object # has no method 'append' ".
It is the navigation of a cms I cant change the markup on.
try this:
$links = $('li.has-child').children('a:first-child');
if($links.length > 0){
$links.each(function(i,link){
$(link).next().prepend($('<li></li>').append($(link)))
})
}
http://jsfiddle.net/wF4FH/6/
You need .clone() method to copy elements..
UPDATED
$links = $('li.has-child').children('a:first-child');
if($links.length > 0){
$links.each(function(i,link){
$(link).next().prepend($('<li></li>').append($(link).clone()))
})
}
http://jsfiddle.net/wF4FH/7/
When you have a jQuery object and you access it by numeric index, you're left with an HTML element. So $('body')[0] == document.body. This means that when you access pageLinks[x], you're really getting a raw element. This means that you want pageLinks[x].appendChild(pageLinks[i]);, not pageLinks[x].childNodes.append(pageLinks[i]);

Programmatically adding nav bar element in jQuery Mobile

I can create a 2 item nav bar in a jQuery mobile page with the following code snippet:
<div id="nav-bar" data-role="navbar">
<ul id="nav-list">
<li><a id="link1" href="#">Nav 1</a></li>
<li><a id="link2" href="#">Nav 2</a></li>
</ul>
</div>
I am attempting to programatically add a third nav bar element using various versions of the following code:
$("#nav-list").append("<li><a id='newElement' href='link3'>Nav 3</a></li>");
$("#nav-bar").navbar();
//$("#pageName").page();
//$("#pageName").trigger("create");
//$("#nav-list").listview("refresh");
When I execute this I see the "Nav 3" link appear but it does not take on the jQuery mobile formatting of the other links.
Any help would be greatly appreciated.
You should append your HTML in a pagebeforecreate handler before JQM's enhancement starts.
I had lost my mind because of this problem. .navbar() used to work in previous versions, for some reason not any more.
I have made a function whose job is to add a new element and then rebuild navbar. One part of it is taken from someone else so I cant take full responsibility for this code (mathod used for style stripping).
Here's working example: http://jsfiddle.net/Gajotres/V6nHp/
And here's a method used:
var navbarHandler = {
addNewNavBarElement:function(navBarID, newElementID, newElementText) {
var navbar = $("#" + navBarID);
var li = $("<li></li>");
var a = $("<a></a>");
a.attr("id", newElementID).text(newElementText);
li.append(a);
navbar = navbarHandler.clearNavBarStyle(navbar);
navbar.navbar("destroy");
li.appendTo($("#" + navBarID + " ul"));
navbar.navbar();
},
clearNavBarStyle:function(navbar){
navbar.find("*").andSelf().each(function(){
$(this).removeClass(function(i, cn){
var matches = cn.match (/ui-[\w\-]+/g) || [];
return (matches.join (' '));
});
if ($(this).attr("class") == "") {
$(this).removeAttr("class");
}
});
return navbar;
}
}

Jquery - manipulating classes in a simple dropdown menu

All,
I previously posted a question but didnt get an answer (admittedly due to poor wording). Am re-posting after updating/ testing the code a bit more.
I want to build a simple drop down menu. Jsfiddle.
PROBLEM:
The menu drops down & upon reclicking the tab, folds away nicely. However, when the menu is down & another tab is clicks, things go pear shaped, i.e. the other menu does not drop down.
Ive tested the jquery code extensively using firebug & have noticed a lot of abnormalities. E.g. I thought for my purpose, the jquery code:
$('ul', curTab).*** would be the same as $(curTab).children(0).***, sometimes this code runs fine, other times it doesn't.
Consistently Ive noticed the 'addClass' & 'removeClass' method not adding & removing the class as intended.
The if ($('.cTabActive')){...} doesn't work, syntax error?
If I do if ($('.cTabActive', aFileTabs)){...}, this doesn't work either...
Im totally at my wit's end with this supposedly simple code. Help would be greatly appreciated.
Code:
(Please review the jsfiddle.net code above)
HTML:
<div id="filemenu"> <!-- right tabs menu -->
<ul id="fm_ul">
<li class="filetabs">File
<ul class='cDropDownItems'>
<li class="m_items"><span class="aHeading">New</span><span class="shortcutK">Ctrl-U</span></li>
<li class="m_items"><span class="aHeading">Open</span><span class="shortcutK">Ctrl-Z</span></li>
</ul></li><li class="filetabs">Edit
<ul class='cDropDownItems'>
<li class="m_items"><span class="aHeading">Undo</span><span class="shortcutK">Ctrl-M</span></li>
</ul></li><li class="filetabs cLastFileTabs">Settings
<ul class='cDropDownItems'>
<li class="m_items m_itemsCK" id="frontView"><img src="Img/tickB&W1.png" alt="tick" /><div class="filler"></div><span class="aHeading">Front View</span><span class="shortcutK">Ctrl-A</span></li>
</ul></li>
</ul>
</div> <!-- close -> 'filemenu' div -->
</div> <!-- close -> 'menu_bars' div -->
JAVASCRIPT:
$('.filetabs').on('click', function (e) {
function abc() {
if ($(curTab).hasClass ('cLastFileTabs')) {
$('ul', curTab).css ({left: '-66.6%'});
$('ul',curTab).animate ({opacity: 1}, 125, 'linear');
} else {
$('ul', curTab).css ({left: 0});
$('ul', curTab).animate ({opacity: 1}, 125, 'linear');
} }
var aFileTabs = $('.filetabs');
var curTab = $(this);
if ($('.cTabActive')) {
var prevDropDown = $('.cTabActive').parent();
var prevDDChild = $(prevDropDown).children(0);
$(prevDDChild).removeClass('cTabActive').addClass('cPrevTabActive'); }
if ($('ul',aFileTabs).hasClass('cPrevTabActive')) {
$('.cPrevTabActive',aFileTabs).animate ({opacity: 0}, 500, 'linear', function () {
$('ul', aFileTabs).css ({left: '9999px'});
$('ul', aFileTabs).removeClass ('cPrevTabActive'); }); }
if ($(prevDropDown).get(0) !== $(curTab).get(0)) {
$(curTab).children(0).addClass ('cTabActive');
abc();
} else {
return;
} });
Kayote,
Here is a working sample. I added .length(), redid the If logic, and use the setTimeout function so fade-in and fade-out does not conflict.
DemoGood Luck!

Categories

Resources