How i make the drop menu Show up smoothly with pure JS? - javascript

I made a drop Menu, the dropped part has display: none; when it be Clicked the JS give it class motherHover what give it display: grid; to make it visible.
But the problem is that the menu appears One-shot.
How to make the drop menu Show up smoothly with pure JS?
function fgf(){
var hoverElement = document.querySelector(".father"),
tragtedElement = document.querySelector("#dds");
tragtedElement.classList.toggle("motherHover");
}
ul{
list-style: none;
}
.father{
}
.mother{
display: none;
}
.asd {
display: grid;
}
.motherHover{
display: grid;
}
<link href="https://meyerweb.com/eric/tools/css/reset/reset200802.css" rel="stylesheet"/>
<ul>
<li class="father" onclick="fgf()">
<button>
Drop Menu
</button>
</li>
<li id="dds" class="mother" >
<ul class="asd">
<i>g</i>
<i>g</i>
<i>g</i>
<i>g</i>
<i>g</i>
</ul>
</li>
</ul>

Its usually done with max-height and transitions.
function fgf(){
let asd = document.querySelector(".asd");
asd.classList.toggle("open");
}
ul{
list-style: none;
}
.asd {
display: grid;
max-height: 0;
transition: max-height 500ms;
overflow: hidden;
background: green;
color: white;
}
.open {
max-height: 100px !important;
}
<link href="https://meyerweb.com/eric/tools/css/reset/reset200802.css" rel="stylesheet"/>
<ul>
<li class="father" onclick="fgf()">
<button>
Drop Menu
</button>
</li>
<li id="dds" class="mother" >
<ul class="asd">
<i>g</i>
<i>g</i>
<i>g</i>
<i>g</i>
<i>g</i>
</ul>
</li>
</ul>

Related

How to prevent link from opening in nested <ul> if <li> hasClass

how to prevent opening link if parent li hasClass: has-menu ? it must work for all nested level
in example below only Sub Sub cat 1/1 and Sub cat 1/2 should open link but if i make it work then collapse with + and - is broken , how to combinate both collapse and link opening only if li dont have class: has-menu
$('li').click(function(e){
if ($(this).hasClass('has-menu')) {
e.preventDefault();
e.stopPropagation();
$(this).children("div").children("ul").toggleClass("menu-close");
$(this).toggleClass("menu-open");
}
})
ul{
padding: 0px;
list-style: none;
}
ul li{
padding: 5px 0;
}
ul li a{
width: 100%;
display: inline-block;
}
.menu-close{
display: none;
/*background-color:red;*/
}
/* Main menu */
.menu-main{
width: 250px;
}
.menu-main li{
position: relative;
}
.has-menu:before{
content: '+';
position: absolute;
right: 0;
}
.has-menu.menu-open:before{
content: '-' !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="menu-main">
<li class="has-menu">
Test 1
<div class="menu-wrapper">
<ul class="menu-child menu-close">
<li class="has-menu">
Sub cat 1/1
<div class="menu-wrapper">
<ul class="menu-subchild menu-close">
<li>
Sub Sub cat 1/1
</li>
</ul>
</div>
</li>
</ul>
<ul class="menu-child menu-close">
<li>
Sub cat 1/2
</li>
</ul>
</div>
</li>
</ul>
.has-menu>a{
pointer-events: none;
}
Try this.
just disable pointer-events on anchor tags that have parent with class .has-menu

classLList.toggle property not working with getElementsByClassName

My HTML Code is for navigation bar:
<Nav>
<ul>
<li onclick="showDropDownMenu('dropdown-menu')" class="dropdown nav-icon">
<span class="fa fa-fw fa-bars"></span>
<ul class="dropdown-menu">
<li>Home</li>
<li>Games</li>
<li>Workout</li>
<li>Yoga</li>
<li>About Us</li>
</ul>
</li>
<li class="home nav-icon"><i class="fa fa-fw fa-home"></i></li>
<li class="active nav-btn">Home</li>
<li class="nav-btn">Games</li>
<li class="nav-btn">Stay Fit</li>
<li class="nav-btn">About Us</li>
</ul>
<form class="nav-search">
<input type="search" name="Search" class="text-area" placeholder="Search...">
<span class="fa fa-fw fa-search search-btn"></span>
</form>
</Nav>
And my css for second ul element
nav .dropdown ul{
display: none;
position: absolute;
background: black;
min-width: 150px;
left: 0;
box-shadow: 5px 8px 16px 0px rgba(0,0,0,0.5);
z-index: 1;
transition: visibility 0.3s linear;
}
.show{
display: block;
}
My Js code is
showDropDownMenu.js
function showDropDownMenu(className)
{
document.getElementsByClassName(className)[0].classList.toggle("show");
};
And it doesn't work. dropdown doesn't apear at all. I don't know what's going on. Have been looking for the error for a day now.
But if i use the display.block property it works. Js looks like this --
function showDropDownMenu(className)
{
var x = document.getElementsByClassName(className);
if(x[0].style.display = "none"){
x[0].style.display = "block";
}
else
{
x[0].style.display = "none";
}
}
Above works fine but the problem is I have to double click to one every page reload to make the dropdown furst appear.
Your javascript code is actually working. But the issue is that your css for nav .dropdown ul having display: none have more weight than display: block of .show class when applied. That's why even after the toggleClass gets executed, your element is not getting displayd in DOM. Because even after the display: block of show class is applied, the dsplay: none of nav .dropdown ul will be the active style, since the class selection have more weight. The best way to fix this out is to make .show class more specific. That is instead of show use nav .dropdown ul.show this will give more priority to display property of show class when applied.
function showDropDownMenu(className) {
document.getElementsByClassName(className)[0].classList.toggle("show");
}
nav .dropdown ul {
display: none;
position: absolute;
background: black;
min-width: 150px;
left: 0;
box-shadow: 5px 8px 16px 0px rgba(0, 0, 0, 0.5);
z-index: 1;
transition: visibility 0.3s linear;
}
nav .dropdown ul.show {
display: block;
}
<nav>
<ul>
<li onclick="showDropDownMenu('dropdown-menu')" class="dropdown nav-icon">
Click Here
<span class="fa fa-fw fa-bars"></span>
<ul class="dropdown-menu">
<li>Home</li>
<li>Games</li>
<li>Workout</li>
<li>Yoga</li>
<li>About Us</li>
</ul>
</li>
<li class="home nav-icon"><i class="fa fa-fw fa-home"></i></li>
<li class="active nav-btn">Home</li>
<li class="nav-btn">Games</li>
<li class="nav-btn">Stay Fit</li>
<li class="nav-btn">About Us</li>
</ul>
<form class="nav-search">
<input type="search" name="Search" class="text-area" placeholder="Search...">
<span class="fa fa-fw fa-search search-btn"></span>
</form>
</nav>
The CSS code is wrong, is display: block without quotes.
It's generally accepted as bad practice to use inline event listeners in JavaScript or to put inline CSS styles directly on an HTML element. Keeping your code separated makes it easier to debug and avoids CSS specificity issues. I'd suggest restructuring your code like this (I added comments where code should be changed):
<nav>
<ul>
<!-- Remove event listener from HTML -->
<li class="dropdown nav-icon">
Click Here
<span class="fa fa-fw fa-bars"></span>
<!-- Add 'hide' class to hide dropdown on page load -->
<ul class="dropdown-menu hide">
<li>Home</li>
<li>Games</li>
<li>Workout</li>
<li>Yoga</li>
<li>About Us</li>
</ul>
</li>
<li class="home nav-icon"><i class="fa fa-fw fa-home"></i></li>
<li class="active nav-btn">Home</li>
<li class="nav-btn">Games</li>
<li class="nav-btn">Stay Fit</li>
<li class="nav-btn">About Us</li>
</ul>
<form class="nav-search">
<input type="search" name="Search" class="text-area" placeholder="Search...">
<span class="fa fa-fw fa-search search-btn"></span>
</form>
</nav>
nav .dropdown ul {
/* Remove "display: none" from this selector */
position: absolute;
background: black;
min-width: 150px;
left: 0;
box-shadow: 5px 8px 16px 0px rgba(0, 0, 0, 0.5);
z-index: 1;
transition: visibility 0.3s linear;
}
.show {
/* Remove quotes around 'block' */
display: block;
}
/* Create new hide class */
.hide {
display: none;
}
function showDropDownMenu(className) {
var x = document.querySelector(className);
// Toggle hide and show classes
if(x.classList.contains('hide')) {
x.classList.remove('hide')
x.classList.add('show');
} else {
x.classList.remove('show')
x.classList.add('hide');
}
}
// Get li.dropdown and add click listener to it
// call showDropDownMenu with the dropdown-menu ul on every click
document.querySelector('.dropdown').addEventListener('click', function() {
showDropDownMenu('.dropdown-menu');
});
This works, while also keeping your code neatly separated. If you're familiar with jQuery, you can dispense altogether with the .hide and .show classes, since jQuery provides a toggle() function that does this automatically for you:
<!-- Add jQuery script to the head of your HTML
Get jQuery link at https://code.jquery.com/
-->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
// JavaScript file reduced to THREE lines
$('nav ul li.dropdown').click(() => {
$('.dropdown-menu').toggle();
})
If you choose the jQuery route, you should remove the .show and .hide classes from your CSS and HTML, and the nav .dropdown ul would need to have display: none on it:
nav .dropdown ul{
/* Add display: none back if using jQuery */
display: none;
position: absolute;
background: black;
min-width: 150px;
left: 0;
box-shadow: 5px 8px 16px 0px rgba(0,0,0,0.5);
z-index: 1;
transition: visibility 0.3s linear;
}
/* .show{
display: block;
}
.hide {
display: none;
} */
You need to apply display:none inline so the display style property returns something:
function showDropDownMenu(className)
{
var x = document.getElementsByClassName(className);
if(x[0].style.display = "none"){
x[0].style.display = "block";
}
else
{
x[0].style.display = "none";
}
}
nav .dropdown ul {
display: none;
position: absolute;
background: black;
min-width: 150px;
left: 0;
box-shadow: 5px 8px 16px 0px rgba(0, 0, 0, 0.5);
z-index: 1;
transition: visibility 0.3s linear;
}
.show {
display: block;
}
<Nav>
<ul>
<li onclick="showDropDownMenu('dropdown-menu')" class="dropdown nav-icon">
<span class="fa fa-fw fa-bars"></span>
<ul class="dropdown-menu" style="display:none">
<li>Home</li>
<li>Games</li>
<li>Workout</li>
<li>Yoga</li>
<li>About Us</li>
</ul>
</li>
<li class="home nav-icon"><i class="fa fa-fw fa-home"></i></li>
<li class="active nav-btn">Home</li>
<li class="nav-btn">Games</li>
<li class="nav-btn">Stay Fit</li>
<li class="nav-btn">About Us</li>
</ul>
<form class="nav-search">
<input type="search" name="Search" class="text-area" placeholder="Search...">
<span class="fa fa-fw fa-search search-btn"></span>
</form>
</Nav>

dropdown menu not opening and closing correctly

I have gotten it to open and close when hovering over the nav link but how do I keep it open so I can access the content on the menu? I need it to work exactly how a dropdown menu works.
This is what I have done so far I also need the html layout to stay the same.
$('.nav-link--dropdown').mouseover(function() {
$('.dropdown-menu').css('display', 'block');
});
$('.nav-link--dropdown').mouseleave(function() {
$('.dropdown-menu').css('display', 'none');
});
ul {
list-style: none;
padding: 0px;
display: flex;
}
li {
margin-right: 10px;
}
.dropdown-menu {
display: none;
}
.dropdown-menu ul {
display: block;
background-color: grey;
color: white;
padding: 10px;
text-align: center;
width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="nav">
<ul>
<li class="nav-link">home</li>
<li class="nav-link--dropdown">dropdown</li>
</ul>
</div>
<div class="dropdown-menu">
<ul>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
</ul>
</div>
View on jsFiddle
Should remove margin use padding for that, if not when we enter that margin area mouse leave event will trigger.
$('.nav-link--dropdown').mouseover(function () {
$('.dropdown-menu').css('display', 'block');
});
$('.dropdown-menu').mouseover(function () {
$('.dropdown-menu').css('display', 'block');
});
$('.nav-link--dropdown').mouseleave(function () {
$('.dropdown-menu').css('display', 'none');
});
$('.dropdown-menu').mouseleave(function () {
$('.dropdown-menu').css('display', 'none');
});
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
ul {
list-style: none;
padding: 0px;
display: flex;
}
li {
padding: 10px;
}
.dropdown-menu {
display: none;
}
.dropdown-menu ul {
display: block;
background-color: grey;
color: white;
padding: 10px;
text-align: center;
width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="nav">
<ul>
<li class="nav-link">home</li>
<li class="nav-link--dropdown">dropdown</li>
</ul>
</div>
<div class="dropdown-menu">
<ul>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
</ul>
</div>
The problem you have here is that you leave the nav-link--dropdown when you move to your dropdown-menu. The simple solution: Include your dropdown-menu in your nav item.
$('.nav-link--dropdown').mouseover(function() {
$('.dropdown-menu').css('display', 'block');
});
$('.nav-link--dropdown').mouseleave(function() {
$('.dropdown-menu').css('display', 'none');
});
ul {
list-style: none;
padding: 0px;
display: flex;
}
li {
margin-right: 10px;
}
.dropdown-menu {
display: none;
}
.dropdown-menu ul {
display: block;
background-color: grey;
color: white;
padding: 10px;
text-align: center;
width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="nav">
<ul>
<li class="nav-link">home</li>
<li class="nav-link--dropdown">
dropdown
<div class="dropdown-menu">
<ul>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
</ul>
</div>
</li>
</ul>
</div>
You can achieve this by bundling the menu item you want to hover over and it's dropdown in the same div.
<div class="nav">
<ul>
<li class="nav-link">home</li>
<div class="innerNav">
<li class="nav-link--dropdown">dropdown</li>
<div class="dropdown-menu">
<ul>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
<li class="dropdown-menu__link">random text</li>
</ul>
</div>
</div>
</ul>
</div>
And:
$('.innerNav').mouseover(function () {
$('.dropdown-menu').css('display', 'block');
});
$('.innerNav').mouseleave(function () {
$('.dropdown-menu').css('display', 'none');
});
See this JSFiddle: https://jsfiddle.net/rmdfqh6c/
Since your problem is the mouseleave event will trigger when you try to move the mouse to the bellow dropdown menu, if you really must keep the elements separated, add the dropdown-menu to the class that keeps the display:block
$('.nav-link--dropdown, .dropdown-menu').mouseover(function () {
$('.dropdown-menu').css('display', 'block');
});
$('.nav-link--dropdown, .dropdown-menu').mouseleave(function () {
$('.dropdown-menu').css('display', 'none');
});
And remove margin/padding from menu items. Use line-height instead if you want some spacing:
.nav ul li {
line-height: 40px;
}
ul {
list-style: none;
margin: 0; // <--- I changed this from padding: 0px;
display: flex;
}
Otherwise the other suggested solutions to include the dropdown within you parent <ul> element is the best.
JSFiddle

How to reverse action on sibling element by jquery

I want to create a menu bar with sub menu using Jquery. There are two item with sub menu. When i click on once i want it slide down and when i click second one then second one need to slide down but first one or all another one need to slide up. I tried following:
HTML
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div class="footer-lang">
<ul>
<li class="lang">
ENG <i class="fa fa-caret-down" aria-hidden="true"></i>
<ul>
<li>En</li>
<li>Bn</li>
</ul>
</li>
<li class="lang">
USD <i class="fa fa-caret-down" aria-hidden="true"></i>
<ul>
<li>USD</li>
<li>VND</li>
</ul>
</li>
</ul>
</div>
CSS
.footer-lang ul{
display: block;
text-align: center;
}
.footer-lang .lang{
display: inline-block;
}
.footer-lang .lang a{
display: block;
padding: 8px;
color: #000;
}
.footer-lang .lang > ul{
display: none;
position: absolute;
}
Javascript
$(document).ready(function(){
$('.footer-lang .lang a').on('click', function(e) {
$(this).next('ul').slideToggle('slow');
e.preventDefault();
});
});
On click make other submenu to slideUp fast or slow and than toggle see below snippet for more info
$(document).ready(function(){
$('.footer-lang .lang a').on('click', function(e) {
$(".lang > ul").slideUp("fast");
$(this).next('ul').slideToggle('slow');
e.preventDefault();
});
});
.footer-lang ul{
display: block;
text-align: center;
}
.footer-lang .lang{
display: inline-block;
}
.footer-lang .lang a{
display: block;
padding: 8px;
color: #000;
}
.footer-lang .lang > ul{
display: none;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div class="footer-lang">
<ul>
<li class="lang">
ENG <i class="fa fa-caret-down" aria-hidden="true"></i>
<ul>
<li>En</li>
<li>Bn</li>
</ul>
</li>
<li class="lang">
USD <i class="fa fa-caret-down" aria-hidden="true"></i>
<ul>
<li>USD</li>
<li>VND</li>
</ul>
</li>
</ul>
</div>
one method is to add a class signifying open when clicking it and then close that class before opening another item. something like this would use that:
$(document).ready(function() {
$('.footer-lang .lang a').on('click', function(e) {
e.preventDefault();
// close the open
$('.is-open').removeClass('is-open').slideToggle('slow);
// open the closed
$(this).next('ul').slideToggle('slow');
$(this).addClass('is-open');
});
});

jQuery slideToggle jump animation on menu toggle

So my problem is just as simple as the title, when I click the big black "V" to toggle my menu, the animation jumps on the <li> with the sub menu.
I tried multiple things and none seem to work, I had a similar issue with the width which I solved by setting width to 100%. On my search I saw people that jQuery can't figure out the height of the elements but if I try to add height to the elements it totaly breaks the menu.
Here's the codepen so you can experience my problem. CODEPEN
HTML
<nav class="main-menu">
<ul>
<li class="level-1">Item 1
<span class="toggle-1">V</span>
<ul class="sub-menu-1">
<li class="level-2">Item 1.1
<span class="toggle-2">V</span>
<ul class="sub-menu-2">
<li class="level-3">Item 1.1.1</li>
<li class="level-3">Item 1.1.2</li>
<li class="level-3">Item 1.1.3</li>
<li class="level-3">Item 1.1.4</li>
</ul>
</li>
<li class="level-2">Item 1.2
<span class="toggle-2">V</span>
<ul class="sub-menu-2">
<li class="level-3">Item 1.2.1</li>
<li class="level-3">Item 1.2.2</li>
</ul>
</li>
<li class="level-2">Item 1.3</li>
</ul>
</li>
<li class="level-1">Item 2
<span class="toggle-1">V</span>
<ul class="sub-menu-1">
<li class="level-2">Item 2.1</li>
<li class="level-2">Item 2.2</li>
<li class="level-2">Item 2.3</li>
<li class="level-2">Item 2.4</li>
<li class="level-2">Item 2.5</li>
</ul>
</li>
<li class="level-1">Item 3</li>
<li class="level-1">BItem 4
<span class="toggle-1">V</span>
<ul class="sub-menu-1">
<li class="level-2">Item 4.1</li>
<li class="level-2">Item 4.2</li>
</ul>
</li>
</ul>
</nav>
CSS
.main-menu .sub-menu-1, .main-menu .sub-menu-2 {
display: none;
}
.main-menu ul {
width: 100%;
}
.main-menu li {
line-height: 25px;
}
.main-menu li.level-1 {
background-color: red;
}
.main-menu li.level-2 {
background-color: lightblue;
}
.main-menu li.level-3 {
background-color: yellow;
}
.main-menu a {
display: block;
margin-right: 20px;
}
.main-menu .toggle-1, .main-menu .toggle-2 {
display: block;
float: right;;
font-size: 20px;
font-weight: bold;
width: 20px;
transform: translateY(-25px);
}
Javascript/jQuery
$(document).ready(function() {
$('.sub-menu-1, .sub-menu-2').hide();
$('.toggle-1').click( function() {
$(this).next('.sub-menu-1').slideToggle();
});
$('.toggle-2').click( function() {
$(this).next('.sub-menu-2').slideToggle();
});
});
Thank you in advance and hope somenone can help me out.
The issue is arising from the <span> that you have set to display: block; in your css.
Switching .main-menu a and .main-menu .toggle-1, .main-menu .toggle-2 to both be display: inline-block; fixes the issue. You can then also remove some additional unnecessary css hacks that you were previously using to forcibly place these elements outside of their document flow position.
The updated CSS is below, and you can see a fork of the pen with both sub-menus working here
.main-menu a {
display: inline-block;
}
.main-menu .toggle-1, .main-menu .toggle-2 {
display: inline-block;
float: right;
font-size: 20px;
font-weight: bold;
width: 20px;
}
I think the only problem is that you didn't set the clear property to your float. If you set it like:
.main-menu a {
margin-right: 20px;
}
.main-menu .toggle-1, .main-menu .toggle-2 {
float: right;
clear: none;
font-size: 20px;
font-weight: bold;
width: 20px;
}
you are good. Then you also don't need the transform: translate to shift your elements back. Here's a fork of your codepen: http://codepen.io/anon/pen/WwbPLV

Categories

Resources