jquery slideToggle() not working with toggleClass - javascript

I wanted to slideToggle menu items with toggleclass, .opened class should be added and removed for menu items. This is working for me when I toggle different menu item but for same menu item when I click this, .opened class won't get removed here is my code
Html menu tag
<ul id="menu-main-menu">
<li class="menu-item"><a href="link_url">text<a>
<ul class="sub-menu">
<li class="menu-item">
<ul class="sub-menu">
<li class="menu-item"><a href="link_url">second sub item<a></li>
</ul>
</li>
<li class="menu-item"><a href="link_url">first sub item<a></li>
<li class="menu-item"><a href="link_url">first sub item<a></li>
</ul>
</li>
<li class="menu-item"><a href="link_url">text<a></li>
</ul>
jquery code
$('.menu-item').on('click', function(e) {
$('.menu-item').removeClass('opened')
$(this).toggleClass('opened');
if ($('.sub-menu', this).length >=1) {
e.preventDefault();
}
$(this).children('ul').slideToggle('fast');
$(this).siblings('li').find('ul').hide('slow')
e.stopPropagation();
});
I am not sure what I am doing wrong. Can you please help me for this?
Thanks

There is a basic mistake in your code.
Close Anchor tags, you have an opening anchor tag on both the ends.
then use the logic to get your result, see the example, If need anything else, please let me know
Add sub items Achor or li text, that depends on your requirement, but for UX you should add some text so users can get that there is still some more content to see.
$('.menu-item').click(function(e){
$(this).siblings().find('> .sub-menu').slideUp();
$(this).find('> .sub-menu').slideToggle();
$(this).siblings().removeClass('opened');
$(this).toggleClass('opened');
e.stopPropagation();
});
.sub-menu {
display: none;
}
.menu-item a{
display: inline-block;
margin-bottom: 10px;
}
.menu-item {
margin-bottom: 10px;
}
.menu-item.hasSubmenu {
border-bottom: 1px solid;
}
.menu-item a {
background-color: red;
color: white;
}
.hasSubmenu {
position: relative;
}
.hasSubmenu:after {
position: absolute;
right: 10px;
top: 0px;
content: "+";
display: block;
font-size: 20px;
font-weight: bold;
}
.hasSubmenu.opened:after {
content: "-";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="menu-main-menu">
<li class="menu-item hasSubmenu">
text
<ul class="sub-menu">
<li class="menu-item hasSubmenu">
First level
<ul class="sub-menu">
<li class="menu-item">second sub item</li>
<li class="menu-item">second sub item</li>
</ul>
</li>
<li class="menu-item">first sub item</li>
<li class="menu-item">first sub item</li>
</ul>
</li>
<li class="menu-item hasSubmenu">
text
<ul class="sub-menu">
<li class="menu-item hasSubmenu">
First level
<ul class="sub-menu">
<li class="menu-item">second sub item</li>
</ul>
</li>
<li class="menu-item">first sub item</li>
<li class="menu-item">first sub item</li>
</ul>
</li>
</ul>

$('.menu-item').on('click', function(e) {
$(this).toggleClass('opened');
$('.menu-item').not($(this)).removeClass('opened');
if ($('.sub-menu', this).length >= 1) {
e.preventDefault();
}
$(this).children('ul').slideToggle('fast');
$(this).siblings('li').find('ul').hide('slow')
e.stopPropagation();
});
Change the order of removing classes, then skip the current element.

Related

toggle menu with JavaScript. Target multiple variables with 1 click function

I am trying to make a menu that collapses on click.
I also want to add some more changes on that same function.
For instance I want to change the background of another object.
In this snippet you can see it works on only the first link. The other toggleable link is not targeted.
var pill = document.querySelector(".navpill");
var sub = document.querySelector(".submenu");
pill.onclick = () => {
sub.classList.toggle("collapse");
pill.classList.toggle("active");
}
.mainmenu {
background-color: #1f1f1f;
}
.navpill {
padding: 15px;
}
.navpill.active {
background: red;
}
.navpill a {
text-decoration: none;
color: white;
}
.submenu {
display: none;
}
.submenu.collapse {
display: block;
}
<div>
<ul class="mainmenu">
<li class="navpill">Link collapse 1
<ul class="submenu">
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
</ul>
</li>
<li class="navpill">Link collapse 2
<ul class="submenu">
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
</ul>
</li>
<li class="navpill">no link</li>
<li class="navpill">no link</li>
</ul>
</div>
From a previous answer I got this piece of code which makes it work on all the links, but I have no idea how to add more var and toggles to the function.
var pills = document.querySelectorAll(".expand");
pills.forEach(function(pill) {
pill.onclick = () => {
var sub = pill.querySelector(".submenu");
sub.classList.toggle("collapse");
}
});
I tried adding this to the code but it does not work.
var navpill = pill.querySelector(".navpill");
navpill.classList.toggle("active");
If possible I would also like a way of clearing what has been done when clicked on the next submenu.
If I use the code above. It stays open when I click on the second link and then they are both open. I want the first one to close if the second is clicked.
I think this is probably closer to what you want.
(It's unclear if you wanted the submenu items to be highlighted when they're clicked - currently, clicking them just collapses the menu anyway so you wouldn't see. Also I removed the hrefs because they aren't adding anything useful.)
var pills = document.querySelectorAll(".expand");
var subs = document.querySelectorAll(".submenu");
pills.forEach(function(pill) {
pill.addEventListener("click", function() {
var sub = pill.querySelector(".submenu");
var alreadyOpen = false;
if (sub.classList.contains("collapse")) alreadyOpen = true;
pills.forEach(function(pill2) {
pill2.classList.remove("active");
});
subs.forEach(function(sub2) {
sub2.classList.remove("collapse");
});
if (!alreadyOpen) {
sub.classList.toggle("collapse");
this.classList.add("active");
}
});
});
.expand.active {
background-color: red;
}
.expand.active > .submenu
{
background-color: #1f1f1f;
}
.mainmenu {
background-color: #1f1f1f;
}
.navpill {
padding: 15px;
color: white;
}
.submenu {
display: none;
}
.submenu.collapse {
display: block;
}
<div>
<ul class="mainmenu">
<li class="navpill expand">Link collapse 1
<ul class="submenu">
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
</ul>
</li>
<li class="navpill expand">Link collapse 2
<ul class="submenu">
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
<li class="navpill">sub Link 1</li>
</ul>
</li>
<li class="navpill">no link</li>
<li class="navpill">no link</li>
</ul>
</div>

How to add event listener with same class?

I have two li with class="tree" in a ul, I want to set a height" to ul.sub-menu when someone clicks on its parent li. But It not working for the second li. Run snippet
document.querySelector(".tree").addEventListener("click", function () {
var height = document.querySelector(".sub-menu").scrollHeight;
document.querySelector(".sub-menu").style.height = height + "px";
});
.sub-menu {
position: relative;
background-color: aqua;
width: 100px;
height: 0;
overflow: hidden;
transition: height 0.3s;
}
<ul>
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item1</li>
<li>Sub Item1</li>
<li>Sub Item1</li>
</ul>
</li>
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item2</li>
<li>Sub Item2</li>
<li>Sub Item2</li>
</ul>
</li>
</ul>
querySelector will return only the first element.
However it is recommended to delegate from the closest common static container - this is also much more elegant than looping over all elements to add eventListeners to each
document.getElementById("topUl").addEventListener("click", function (e) {
const tgt = e.target.closest("li");
if (tgt.classList.contains("tree")) {
const sm = tgt.querySelector(".sub-menu");
const height = sm.scrollHeight;
sm.style.height = height + "px";
}
});
.sub-menu {
position: relative;
background-color: aqua;
width: 100px;
height: 0;
overflow: hidden;
transition: height 0.3s;
}
<ul id="topUl">
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item1</li>
<li>Sub Item1</li>
<li>Sub Item1</li>
</ul>
</li>
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item2</li>
<li>Sub Item2</li>
<li>Sub Item2</li>
</ul>
</li>
</ul>
All you need to do is use querySelectorAll to select all the classes, and add eventListener inside loop
document.querySelectorAll(".tree").forEach(i => i.addEventListener(
"click",/*your code here*/));
Solution using forEach() method.
let tree = document.querySelectorAll(".tree");
let height = document.querySelectorAll(".sub-menu");
tree.forEach(function(tree_current, index) {
tree_current.addEventListener("click", function () {
height[index].style.height = height[index].scrollHeight + "px";
});
})
.sub-menu {
position: relative;
background-color: aqua;
width: 100px;
height: 0;
overflow: hidden;
transition: height 0.3s;
}
<ul>
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item1</li>
<li>Sub Item1</li>
<li>Sub Item1</li>
</ul>
</li>
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item2</li>
<li>Sub Item2</li>
<li>Sub Item2</li>
</ul>
</li>
</ul>
document.querySelector() returns the first object matching the query, not all of them. You can check them by pasting console.log(document.querySelector('.tree')); at the top of your JS file, and you'll see that it only logs your first element. If you want to add the listener to all of your elements matching the query, you need to use document.querySelectorAll() and iterate over that array using forEach:
document.querySelectorAll('.tree').forEach(el => el.addEventListener('click', () => {
/* Code Here */
}));
Another issue you'll face with your code is that your other query selector also returns the first occurrence of .sub-menu. To fix it, you can change
document.querySelectorAll('.tree').forEach(el => el.addEventListener('click', () => {
/* Code Here */
}));
to
document.querySelectorAll('.tree').forEach(el => {
const submenu = el.querySelector('.sub-menu');
el.addEventListener('click', () => submenu.style.height = submenu.scrollHeight + "px");
});
Since .sub-menu is a child of .tree in your hierarchy, this will only search your .tree for instances of .sub-menu and hence return the correct element you need. Your final code would then look like this:
document.querySelectorAll('.tree').forEach(el => {
const submenu = el.querySelector('.sub-menu');
el.addEventListener('click', () => submenu.style.height = submenu.scrollHeight + "px");
})
.sub-menu {
position: relative;
background-color: aqua;
width: 100px;
height: 0;
overflow: hidden;
transition: height 0.3s;
}
<ul>
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item1</li>
<li>Sub Item1</li>
<li>Sub Item1</li>
</ul>
</li>
<li class="tree">
Item
<ul class="sub-menu">
<li>Sub Item2</li>
<li>Sub Item2</li>
<li>Sub Item2</li>
</ul>
</li>
</ul>
EDIT: Don't repeat yourself. To avoid having to lookup the element every time the button is clicked we cache it as soon as we initialise the listener and the refer to that stored variable in the click handler.
Just use querySelectorAll it is gonna select all classes, and add click event inside it.
document.querySelectorAll(".tree").forEach(
index => index.addEventListener("click",
/*your code here*/
));

onclick expand multiple menu

I want to make an onclick expand multiple menu in my website
Before I follow this thread: this with little bit modify I get:
<ul>
<rg><li id="auctions">Menu</li></rg>
<br></br>
<lf>
<li class="submenu">Left</li>
</lf>
<rg>
<li class="submenu">Right</li>
</rg>
</ul>
But it only shows a menu, then I create a duplicate like this:
<ul>
<rg><li id="auctions">Menu</li></rg>
<br></br>
<lf>
<li class="submenu">Left</li>
</lf>
<rg>
<li class="submenu">Right</li>
</rg>
</ul><ul>
<rg><li id="auctions2">Menu</li></rg>
<br></br>
<lf>
<li class="submenu2">Left</li>
</lf>
<rg>
<li class="submenu2">Right</li>
</rg>
</ul>
And JS and CSS like this:
<script>
$(function() {
$('#auctions').click(function(){
$('.submenu').slideToggle();
});
});
$(function() {
$('#auctions2').click(function(){
$('.submenu2').slideToggle();
});
});
</script>
<style>
.submenu{display:none;}
.submenu2{display:none;}
rg {float:right}
lf {float:left}
</style>
Its work but doesn't run inline. Then I useul {display:inline-block}
Yes, the menu running inline, but it's broken and float doesn't work properly. Can it's fixed? or can I make multiple menu in same <ul>?
make rg and lf as classes and change the html accordingly to get your desired style.
But I recommend, you should consider studying about ul and li tags and its properties before using it
$(function() {
$('#auctions').click(function() {
$('.submenu').slideToggle();
});
});
$(function() {
$('#auctions2').click(function() {
$('.submenu2').slideToggle();
});
});
ul {
display: block;
margin:0;
padding:0;
width:100%;
}
li {
list-style: none;
}
li.main {
width:100%;
text-align:right;
}
.submenu {
display: none;
}
.submenu2 {
display: none;
}
.rg {
float: right;
}
.lf {
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<ul>
<li id="auctions" class="main rg">Menu</li>
<li>
<ul>
<li class="submenu lf">Left</li>
<li class="submenu rg">Right</li>
</ul>
</li>
</ul>
</li>
<li>
<ul>
<li id="auctions2" class="main rg">Menu</li>
<li>
<ul>
<li class="submenu2 lf">Left</li>
<li class="submenu2 rg">Right</li>
</ul>
</li>
</ul>
</li>
</ul>

Animate second level bullet points in slidify

I am trying to get the second (lower) level bullet points in io2012 to animate separately from their parent bullet point, like this:
>* First level animates by itself
>+ Second level then animates by itself
>* Another first level animates by itself
I've tried several workarounds with HTML like using >* in place of >+ while attempting to indent the bullet with <div style="padding-left: 1em">>* Second level animated by itself.
However this just indents the text but not the bullet point. My experimentation with <li style="padding-left: 1em">...</li> similarly failed.
If there is no HTML solution, does the solution involve either of CSS or JavaScript?
If you are willing to go with a slightly hacky workaround, I have had success inserting .fragment at the start of paragraphs and bullets that I wanted to animate (some other things with my slides were conflicting with the >- shortcut, though I still have not figured out what).
In any case, this should work, even if it is a bit kludgy.
- .fragment First level animates by itself
- .fragment Second level then animates by itself
- .fragment Another first level animates by itself
(.fragment adds a div class "fragment" to the following paragraph or item)
If you want a sub level menu to increment, you could set a counter-increment in the css like demonstrated in the following snippet:
ol {
counter-reset: item
}
li {
display: block;
}
li:before {
content: counters(item, ".")" ";
counter-increment: item
}
<ol>
<li>one</li>
<li>two
<ol>
<li>two.one</li>
<li>two.two</li>
<li>two.three</li>
</ol>
</li>
<li>three
<ol>
<li>three.one</li>
<li>three.two</li>
<ol>
<li>three.two.one</li>
<li>three.two.two</li>
</ol>
</ol>
</li>
<li>four</li>
</ol>
However if numerical lists is not what you had in mind, there are a number of ways you can increment a list using various list-style types
h2.title {
font-size: 20px;
font-weight: 800;
margin-left:-20px;
padding: 12px;
counter-increment: ordem;
}
li.heading {
font-size: 16px;
font-weight: bold;
padding: 12px;
list-style-type:none;
}
.bullet {
counter-reset: bullet;
padding-left: 12px;
}
.bullet li {
list-style-type: none;
}
.bullet li:before {
counter-increment: bullet;
content: counter(ordem)"." counter(bullet)" ";
}
ol.none {
list-style:none!important
}
li.s2sub::before {
counter-increment:none!important;
content:none!important;
}
li.s2sub {
list-style:upper-alpha;
}
li.s3sub::before {
counter-increment:none!important;
content:none!important;
}
li.s3sub {
list-style-type:circle;
}
li.roman::before {
counter-increment:none!important;
content:none!important;
}
li.roman {
list-style:lower-roman inside;
}
<body>
<ol>
<h2 class="title">Section 1</h2>
<li class="heading">Heading 1</li>
<ol class="bullet">
<li>text 1 one</li>
<li>text 1 two</li>
<li>text 1 three</li>
<li>text 1 four</li>
</ol>
<li class="heading">Heading 2</li>
<ol class="bullet">
<li class="roman">Item 1</li>
<li class="roman">Item 2</li>
<li class="roman">Item 3</li>
</ol>
<h2 class="title">Section 2</h2>
<ol class="bullet">
<li>First item
<ol>
<li class="s2sub">First subitem</li>
<li class="s2sub">Second subitem</li>
</ol>
</li>
<li>Second Item</li>
<li>Third Item</li>
</ol>
<h2 class="title">Section 3</h2>
<ol class="bullet">
<li>First item
<ol>
<li class="s3sub">First subitem</li>
<li class="s3sub">Second subitem</li>
</ol>
</li>
<li>Second item</li>
<li>Third item</li>
</ol>
</ol>
</body>
Hope this helps

javascript hover function for submenu

I'm pretty new at trying to understand javascript and I've been pooling over multiple examples trying to figure out what I'm doing wrong, but cant get this working properly. At one point I had working with onmouseover/mouseout but it only worked on 1 of the menus.
I'm sure it is something simple I have overlooked, but any help would be appreciated.
http://jsfiddle.net/N3TyT/
jQuery(document).ready(function($) {
$('#top-menu').hover(
function () {
$('#submenu').show(active);
},
function () {
$('#submenu').hide(non-active);
}
);
});
<ul id="menu" class="nav-menu">
<li>Home</li>
<li id="top-menu">About Us
</li>
<ul id="submenu" class="sub-menu non-active">
<li>US</li>
<li>Our Style</li>
<li>The Experience</li>
</ul>
<li id="top-menu">Galleries
</li>
<ul id="submenu" class="sub-menu non-active">
<li>Weddings</li>
<li>Engagements</li>
<li>Featured Weddings</li>
</ul>
<li id="top-menu">The Details
</li>
<ul id="submenu" class="sub-menu non-active">
<li>Investment</li>
<li>Press and Awards</li>
<li>Testimonials</li>
</ul>
<li>FAQ</li>
<li>Contact</li>
<li>The Blog</li>
</ul>
.nav-menu {
list-style-type:none;
text-align:center;
text-transform:uppercase;
font-weight:bold;
font: 24px'Playfair Display', Georgia, serif;
}
.navmenu ul li {
margin:30px;
}
.non-active {
display:none;
}
.active {
display:inline;
}
It doesn't answer your specific question but the same behavior can be easily achieved with css. This way you don't depend on javascript being turned on for standard menu access.
ul.menu li ul {
display: none;
}
ul.menu li:hover ul {
display: block;
position: relative;
}
<ul class="menu">
<li>Home</li>
<li>
Galleries
<ul>
<li>Gallery #1</li>
<li>Gallery #2</li>
</ul>
</li>
<li>
Albums
<ul>
<li>Album #1</li>
<li>Album #2</li>
</ul>
</li>
</ul>
View on jsFiddle
You are using hide and show wrong.
http://api.jquery.com/show/
http://api.jquery.com/hide/
http://jsfiddle.net/eXKV9/
$('#top-menu').hover(
function () {
$('#submenu').show();
},
function () {
$('#submenu').hide();
}
);
id must be unique. If you have multiple elements with the same id, jquery will not retrieve all the elements when you do $('#top-menu'), it'll only find the first element that matches the selector.
We're going to need to change the HTML a bit. IDs are used only once on a page. Classes are similar, but can be applied to any number of elements. We also want to nest our sub-menu's under the top-menu. That way the association is more clear.
<li class="top-menu">About Us
<ul class="sub-menu non-active">
<li>Ashley + David</li>
<li>Our Style</li>
<li>The Experience</li>
</ul>
</li>
We want to specify the nested sub-menu to show or hide. $(this) refers to the top-menu that was hovered over.
$('.top-menu').hover(
function () {
$(this).find('.sub-menu').show("slow");
},
function () {
$(this).find('.sub-menu').hide("slow");
}
);
demo
I updated your work. Is this what are trying to establish?
$('#top-menu').mouseover(function(){
$('#submenu').addClass('active');
});
$('#top-menu').mouseout(function(){
$('#submenu').removeClass('active');
});
JSFiddle Demo

Categories