javascript show and hide sub menu on click - javascript

I have a nav menu bar.
This nav menu contain a sub menu.
When i click on the li the sub menu appear, but when i click again on the li to hide the sub menu this sub menu doesn't hide but if i click on another li the first sub menu disappear.
What i want to make is when i click on the li the sub menu apear and when i click again the sub menu disappear and if i click on another li that contain the class sub it should close any other sub menu and appear the sub menu of this li
i try toggle, classList.contains, if condition but this not work for me
below an example
let menuLi = document.querySelectorAll('.sub'),
subMenu = document.querySelectorAll('.sub-menu');
menuLi.forEach((li) => {
li.addEventListener('click', () => {
menuLi.forEach((li) => {
li.classList.remove('active');
});
if (li.classList.contains('active')) {
li.classList.remove('active');
} else {
li.classList.add('active');
}
})
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
list-style: none;
text-decoration: none;
}
.navbar {
display: flex;
align-items: center;
gap: 20px;
height: 60px;
padding: 10px;
background: red;
}
.sub {
position: relative;
}
.sub .sub-menu {
position: absolute;
top: 39px;
left: 0;
width: 100px;
display: none;
}
.sub .sub-menu li {
margin: 10px 0;
}
.sub.active .sub-menu {
display: block;
}
<ul class="navbar">
<li>
Home
</li>
<li class="sub">
Info
<ul class="sub-menu">
<li>Info 1</li>
<li>Info 2</li>
</ul>
</li>
<li class="sub">
image
<ul class="sub-menu">
<li>img 1</li>
<li>img 2</li>
</ul>
</li>
</ul>

I'd probably just keep previous state, reset state of all and set state accordingly.
menuLi.forEach((li) => {
li.addEventListener('click', () => {
const isOpened = li.classList.contains('active')
menuLi.forEach((li) => {
li.classList.remove('active');
});
if (isOpened) {
li.classList.remove('active');
} else {
li.classList.add('active');
}
})
});

Related

Mobile navigation issues with jQuery

I created a simple navigation, but it doesn't work on mobile. When you click on the plus symbol or "Book 1 name" the list of links are suppose to appear, but for some reason it doesn't work, and when you click the plus symbol the ex symbol is suppose to appear. The book title should link should work on desktop, but not on mobile. Thanks!
$(document).ready(function () {
$(window).resize(function() {
if($(window).width() <= 550) {
$('.bookName').click(function(e){
e.preventDefault();
$('.bookNavigation ul li ul').toggleClass('toggleNav')
$(this).toggleClass('changeIcon');
});
} else {
$('.bookName').click(function(e){
return true;
});
}
});
});
.bookNavigation ul li ul{
display: flex;
}
.toggleNav{
display: block;
}
#media only screen and (max-width: 550px) {
.bookNavigation ul li ul{
display: none;
}
.bookName::before{
content: '+';
padding-right: 10px;
}
.bookName .changeIcon::before{
content: '-';
padding-right: 10px;
}
}
a{
text-decoration: none;
}
li{
list-style: none;
}
.bookNavigation{
margin-bottom: 100px;
}
.bookName{
text-transform: uppercase;
font-size: 30px;
}
li{
margin-right: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav class="bookNavigation">
<ul>
<li>
Book 1 Name
<ul>
<li>Book 1 Chapter 1</li>
<li>Book 1 Chapter 2</li>
<li>Book 1 Chapter 3</li>
</ul>
</li>
</ul>
</nav>
<p>you are in Book 1 Name cover page</p>
You can do this check $(window).width() <= 550 inside the click event rather than attaching the event based on screen-size. Please find the corrected code.
$('.bookName').click(function(e) {
if ($(window).width() <= 550) {
e.preventDefault();
$('.bookNavigation ul li ul').toggleClass('toggleNav')
$(this).toggleClass('changeIcon');
}
});
.bookNavigation ul li ul {
display: flex;
}
.bookNavigation ul li ul.toggleNav {
display: block;
}
#media only screen and (max-width: 550px) {
.bookNavigation ul li ul {
display: none;
}
.bookName::before {
content: '+';
padding-right: 10px;
}
.bookName .changeIcon::before {
content: '-';
padding-right: 10px;
}
}
a {
text-decoration: none;
}
li {
list-style: none;
}
.bookNavigation {
margin-bottom: 100px;
}
.bookName {
text-transform: uppercase;
font-size: 30px;
}
li {
margin-right: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav class="bookNavigation">
<ul>
<li>
Book 1 Name
<ul>
<li>Book 1 Chapter 1</li>
<li>Book 1 Chapter 2</li>
<li>Book 1 Chapter 3</li>
</ul>
</li>
</ul>
</nav>
<p>you are in Book 1 Name cover page</p>
Instead of checking on resize, check the window with inside buttonClickHandler like below
$(button).on('click', function(){
if($(window).width() <= 550){
// Code to toggle list
}
});
Another problem was the specificity of toggleNav css class.
.bookNavigation ul li ul {
display: none;
}
Above code is more specific than
.toggleNav {
display: block;
}
So the toggling toggleNav won't work because the 1st one will override this one. For fixing it create a class like the one below and toggle it on click. So that it will be hidden initially and will be removed on click.
.navHidden {
display: none
}
Check this pin for working example: https://codesandbox.io/s/bold-currying-39w11

How to make CSS drop down menu appear/change on click

I have a web page, 1000px by 1000px for the main div. Inside the main div, at the top, there is a horizontal bar with four sections, each taking up 1/4 of the space. Each section has some text [wrapped in h2 tag], horizontally/vertically centered in the middle of the 1/4 section and each section must generate a drop-down menu.
For the drop down menu [which must work both on mobile and desktop], I borrowed the idea of using a checkbox [check for make dropdown visible, uncheck for invisible], but it isn't working correctly. The checkbox is small and if it is invisible it is almost impossible to know where to click to check/uncheck. I want the drop down menu to appear if the user clicks/taps ANYWHERE in the 1/4 section area.
The horizontal row of 1/4 section drop down menus looks like this:
^ Note that they don't actually work.
HTML Code:
<div id="Media_Choices">
<div id="Video" class="media_choice"> <h2>Video▼</h2> </div>
<div id="Pictures" class="media_choice"> <h2>Pictures▼</h2> </div>
<div id="Audio" class="media_choice"> <h2>Audio▼</h2> </div>
<div id="Stories" class="media_choice"> <h2>Stories▼</h2> </div>
</div>
CSS:
#Media_Choices {
width: 100%;
max-height:40px;
min-height:40px;
}
.media_choice {
display: inline;
float: left;
width: 24.5%;
max-height: 38px;
min-height: 38px;
text-align: center;
vertical-align: middle;
line-height: 38px; /* the same as your div height */
}
#Video {
}
#Pictures {
}
#Audio {
}
#Stories {
}
Extra credit if you can get the ▼ downward facing arrow to turn into a ▲ whenever the drop down menu is down and then revert back into a ▼ downward facing arrow whenever the menu is up. You don't need to use the check-box based technique [I know there is a hover option], but anything that works cross platform is good.
For reference, check boxes were origionally implemented using the following code [taken from another question], but copy-pasting in this solution and changing the text inside the box isn't good enough:
<input class="dropdowninput" type="checkbox" id="dropdownbox1"/>
<div class="dropdownbox">
<label for="dropdownbox1">Open dropdown</label>
<ul class="dropdown">
<li>...</li><li>etc</li>
</ul>
</div>
with CSS:
.dropdowninput, .dropdown {display:none;}
.dropdowninput:checked + .dropdownbox .dropdown {display:block;}
If i understand you correctly you want to create a responsive dropdown menu and you want the arrows to change when the menu appear/disappear, if this is the case a one way to do it would be to attach event listeners to the menu items that would show/hide the submenus on click, using css and javascript you can do the following:
.media_choice > h2:after {
display: inline-block;
content: '▼';
}
.media_choice.dropped > h2:after {
content: '▲';
}
.media_choice > ul {
display: none;
}
.media_choice.dropped > ul {
display: block;
}
And with javascript add the event listeners:
$(document).ready (function()
{
$('.media_choice').on ('click', function()
{
$(this).toggleClass ('dropped');
});
});
JSFiddle
Here it is using checkboxes and no JS.
nav {
width: 80%;
margin: 20px auto;
}
nav ul {
margin: 0;
padding: 0;
list-style-type: none;
overflow: none;
/* to contain the floats */
}
nav li {
padding: 0;
margin: 0;
width: 25%;
float: left;
position: relative;
white-space: nowrap;
}
nav input {
display: none;
}
nav label {
display: block;
border: 1px solid black;
padding: 10px;
cursor: pointer;
}
nav label:hover {
background: #ccc;
}
nav a {
display: block;
padding: 10px;
}
nav a:hover {
background: #ccc;
}
nav label:after {
content: '▼';
font-size: 10px;
}
nav ul ul {
display: none;
position: absolute;
top: 100%;
border: 1px solid red;
box-sizing: border-box;
background: #fff;
width: 100%;
}
nav ul ul li {
width: 100%;
float: none;
}
nav input:checked ~ ul {
display: block;
}
nav input:checked ~ label:after {
content: '▲';
}
<!-- http://codepen.io/allicarn/pen/gPPmZZ -->
<nav>
<ul>
<li>
<input type="checkbox" id="navitem1" name="navinputs" />
<label for="navitem1">Menu Item #1</label>
<ul>
<li>Sub Menu Item #1a</li>
<li>Sub Menu Item #1b</li>
<li>Sub Menu Item #1c</li>
<li>Sub Menu Item #1d</li>
</ul>
</li>
<li>
<input type="checkbox" id="navitem2" name="navinputs" />
<label for="navitem2">Menu Item #2</label>
<ul>
<li>Sub Menu Item #2a</li>
<li>Sub Menu Item #2b</li>
<li>Sub Menu Item #2c</li>
<li>Sub Menu Item #2d</li>
</ul>
</li>
<li>
<input type="checkbox" id="navitem3" name="navinputs" />
<label for="navitem3">Menu Item #3</label>
<ul>
<li>Sub Menu Item #3a</li>
<li>Sub Menu Item #3b</li>
<li>Sub Menu Item #3c</li>
<li>Sub Menu Item #3d</li>
</ul>
</li>
<li>
<input type="checkbox" id="navitem4" name="navinputs" />
<label for="navitem4">Menu Item #4</label>
<ul>
<li>Sub Menu Item #4a</li>
<li>Sub Menu Item #4b</li>
<li>Sub Menu Item #4c</li>
<li>Sub Menu Item #4d</li>
</ul>
</li>
</ul>
</nav>

jQuery slide toggle corresponding service item

Working on a jQuery slide toggle where upon click of an item in a ul toggle down the corresponding item in another ul. I'm having trouble getting the click to be linked to the id and toggling the correct ul item.
jQuery is:
$(document).ready(function() {
//on click of subservices list item toggle down the corresponding subservices item
$(".subServices").find("li").hide().end() // Hide all other ULs
.click(function(e) {
if (this == e.target) { // if the handler element is where the event originated
$(this).children('ul.subServicesList.subServiceItem').slideToggle('fast');
}
});
});
Can anyone help :)
Fiddle here
First, ID of an element must be unique so you can't use same ID for subServices and SubServicesList. In the below solution we uses a data-target attribute for subServices.
Also you need to register the handler to .SubServicesList a element, not the SubServicesList
$(document).ready(function() {
//on click of subservices list item toggle down the corresponding subservices item
$(".subServices > li").hide().end() // Hide all other ULs
$('.SubServicesList a').click(function(e) {
$('#' + $(this).data('target')).slideToggle()
});
});
.SubServicesList {
display: block;
width: 55%;
text-align: center;
margin-left: auto;
margin-right: auto;
}
.SubServicesList li {
display: inline;
list-style: none;
list-style-type: none;
margin-right: 10px;
text-align: center;
line-height: 26px;
cursor: pointer;
}
.SubServicesList li a:hover {
color: #333333;
}
.subServices {
width: 75%;
margin: 0 auto;
}
.subServices li.subServiceItem {
display: block;
list-style: none;
list-style-type: none;
height: auto;
clear: both;
}
.subServices li.subServiceItem .image {
float: left;
display: block;
margin-right: 10px;
margin-bottom: 10px;
}
.subServices li.subServiceItem .image {
float: left;
display: block;
}
/*.subServices li.subServiceItem { display:none; } */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<ul class="SubServicesList">
<li><a data-target="1">Name 1</a></li>
<li><a data-target="2">Name 2</a></li>
<li><a data-target="3">Name 3</a></li>
</ul>
<ul class="subServices">
<li class="subServiceItem" id="1">
1 hwiufhwriufhiurhfiureh
</li>
<li class="subServiceItem" id="2">
2 hwiufhwriufhiurhfiureh
</li>
<li class="subServiceItem" id="3">
3 hwiufhwriufhiurhfiureh
</li>
</ul>
This is how I did it
$(".SubServicesList > li > a").click(function () {
$(".subServices>li").hide();
$(".subServices").find("li[id='" + $(this).attr('id') + "']").slideToggle(true);
});
Here is the JSFiddle

AngularJS top menu submenu dropdowns on hover (persist showing whilst moving to submenu)

I have a top menu with various links. On hover, each should show a dropdown with additional menu items. I have tried attached onmouseover and onmouseleave events to the menu item to hide/show the sub menu; however, when transitioning off of the menu item and into the sub menu, the onmouseleave fires and hides the sub menu and the user doesn't have a chance to actually interact with the sub menu.
<nav>
<div class="container-fluid">
<ul class="">
<li>
<a ui-sref="home.person" ng-init="showPersonSubMenu=false" ng-mouseenter="showPersonSubMenu=true" ng-mouseleave="showPersonSubMenu=false">People</a>
<ul class="person-sub-menu" ng-show="showPersonSubMenu">
<li>Add Person</li>
</ul>
</li>
<li><a ui-sref="home.company">Companies</a></li>
<li><a ui-sref="home.job">Jobs</a></li>
<li><a ui-sref="home.report">Reports</a></li>
</ul>
</div>
</nav>
How can I show the sub menu on hover, and hide it on leaving... whilst still allowing the user to actually access the sub menu so it doesn't hide before they can interact with it.
You were on the right track.
Make sure there is no space between your menu item and your absolute sub-menu. To ensure that there is no space, make the menu item bigger (using height or line-height), or add a padding to it...
Here's a working example:
http://codepen.io/jlowcs/pen/QwJwJZ
HTML:
<ul class="menu">
<li>
<a>People</a>
<ul class="sub-menu">
<li>Add Person</li>
<li>Action 2</li>
</ul>
</li>
<li><a ui-sref="home.company">Companies</a></li>
</ul>
CSS:
ul, li {
list-style-type: none;
margin: 0;
padding: 0;
}
.menu {
background: lightblue;
height: 30px;
padding: 0 10px;
}
.menu > li {
display: inline-block;
width: 100px;
line-height: 30px;
}
.sub-menu {
position: absolute;
display: none;
background-color: lightgreen;
padding: 5px;
}
.sub-menu > li {
display: block;
cursor: pointer;
}
li:hover .sub-menu {
display: block;
}
EDIT: if you want your submenu to float lightly lower, here's a way of doing that:
http://codepen.io/jlowcs/pen/dPQPxW
Just add the following CSS:
.sub-menu {
margin-top: 10px;
}
.menu > li:hover {
padding-bottom: 10px;
}

How to Make a Menu Like Stack Overflow's

I really like the way Stack Overflow has done their dropdown menus on the top. Notice how you must click in order for the dropdown to trigger, but you still must hover to get the dropdown. There is also what seems to be groups - once you click, hover will activate the menus shown in the picture. It is hard to explain, but if you play around for a minute you'll see what I mean. Also, it is important that the last hovered menu will show until a user clicks off again.
Here is what I have so far; note that I almost have the same functionality, except for the last menu hovered doesn't stay dropped (it closes on mouseout when it shouldn't until off-click) and the toggle functionality is sketchy:
$(document).ready(function() {
var depressed = false;
$('.menu').click(function() {
depressed = true;
$('.menu').toggleClass('active');
});
$('.menu').hover(function() {
if (depressed) {
$('.menu').toggleClass('active');
}
});
});
ul {
margin: 0;
padding: 0;
}
ul li {
display: inline-block;
padding: 10px 20px;
background: #333;
color: #eee;
}
ul li.active:hover ul {
display: block;
}
ul li ul {
display: none;
position: absolute;
border: 2px solid #555;
}
ul li ul li {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="menu">button 1
<ul>
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
<li class="menu">button 2
<ul>
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
<li class="menu">button 3
<ul>
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
</ul>
My JS is not cuttin git.
So, what is the best way to go about this?
I haven't peeked into the SO menu, so I don't know how they did it. But this seems to work nicely.
A click on one of the buttons opens the dropdown.
The dropdown is not closed by a 'mouseout', like in your code, but only when another menu is hovered. If you hover outside of the menu area, the lastly hovered menu remains open.
Clicking anywhere closes the menu.
So showing the menu is not directly done by using a :hover pseudo element in CSS, but by adding a class, which remains there even when the menu is unhovered. I think the net result behaves pretty close to Stack Overflow's.
$(function(){
// The event to handle clicks outside of the menu. This will close the menu.
var offEvent =
function(event){
$('.menu-bar').removeClass('active');
$(document).off('click', offEvent);
};
// The click event on the menu buttons, to toggle 'menu mode' as it were.
$(document).on('click', '.menu-bar .menu',
function(event){
event.preventDefault();
$('.menu-bar').addClass('active');
$(this).addClass('active');
// Leave menu mode by clicking anywhere of the menu.
$(document).on('click',
offEvent
);
});
// Hover toggles between the dropdowns of the separate menus.
$('.menu-bar .menu').hover(
function(event){
var $current = $(this);
$('.menu').each(
function(index, element){
var $element = $(this);
if ($element.get(0) == $current.get(0)) {
$element.addClass('active');
} else {
$element.removeClass('active');
}
});
});
});
ul {
margin: 0;
padding: 0;
}
ul li {
display: inline-block;
padding: 10px 20px;
background: #333;
color: #eee;
}
ul li ul {
display: none;
position: absolute;
border: 2px solid #555;
}
ul li ul li {
display: block;
}
.menu .dropdown {
display: none;
}
.menu-bar.active .menu.active .dropdown {
display: block;
}
.menu {
display: inline-block;
position: relative;
border: 1px solid grey;
}
.menu .dropdown {
position: absolute;
top: 100%;
left: 0;
border: 2px solid #555;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="menu-bar">
<li class="menu">button 1
<ul class="dropdown">
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
<li class="menu">button 2
<ul class="dropdown">
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
<li class="menu">button 3
<ul class="dropdown">
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
</ul>
$(document).ready(function() {
$('#M1').click(function() {
$('#M1').toggleClass('active');
$('#M2').removeClass('active');
$('#M3').removeClass('active');
});
$('#M2').click(function() {
$('#M2').toggleClass('active');
$('#M1').removeClass('active');
$('#M3').removeClass('active');
});
$('#M3').click(function() {
$('#M3').toggleClass('active');
$('#M1').removeClass('active');
$('#M2').removeClass('active');
});
});
ul {
margin: 0;
padding: 0;
}
ul li {
display: inline-block;
padding: 10px 20px;
background: #333;
color: #eee;
}
ul li.active ul {
display: block;
}
ul li ul {
display: none;
position: absolute;
border: 2px solid #555;
}
ul li ul li {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="menu" id='M1'>button 1
<ul>
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
<li class="menu" id='M2'>button 2
<ul>
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
<li class="menu" id='M3'>button 3
<ul>
<li>sub button 1</li>
<li>sub button 2</li>
</ul>
</li>
</ul>
This works for me.
I've just removed :hover from the end of the .active class and made an individual function for each button.

Categories

Resources