Hide every menu opened just after click a link from it - javascript

I am making an Off Canvas sidebar dropdown menu in vanilla JS.
Almost at the bottom of my JS code I have made a function to hide the sidebar off canvas just after someone clicks an href link.
Also I have made a function to hide and show the hidden submenu level of the dropdown menu in order to click them.
But now I need a function to close every opened dropdown menu from the sidebar when a user clicks an href link, just before getting the user to anoher site page. Then, when the user opens again the menu all dropdowns will be closed.
NOTE: 'aTags' is the name of the variable that points to every clickeable href element
Any advice will be very helpfull!
const open = document.querySelector('.open');
let list_items = document.querySelectorAll('#subb');
let overlay = document.querySelector('.overlay');
let aTags = open.getElementsByClassName('ilink');
// Show and Hide sidebar when click icon
let toggles = document.querySelectorAll(".c-hamburger");
for (let i = toggles.length - 1; i >= 0; i--) {
let toggle = toggles[i];
toggleHandler(toggle);
};
function toggleHandler(toggle) {
toggle.addEventListener("click", function(e) {
e.preventDefault();
if (this.classList.contains("is-active") === true) {
this.classList.remove("is-active");
open.classList.remove('oppenned');
overlay.className = 'overlay';
} else {
this.classList.add("is-active");
open.classList.add('oppenned');
overlay.className += ' open';
}
});
window.addEventListener('click', function(event) {
if (event.target === overlay) {
toggle.classList.remove("is-active");
open.classList.remove('oppenned');
overlay.className = 'overlay';
}
});
// hide sidebar off canvas when click a menu link
for (let i = 0; i < aTags.length; i++) {
aTags[i].addEventListener("click", close);
}
function close(e) {
e.stopPropagation();
open.classList.remove('oppenned');
toggle.classList.remove("is-active");
overlay.className = 'overlay';
}
// Show and Hide the hidden Submenu ul
for (let i = 0; i < list_items.length; i++) {
list_items[i].addEventListener("click", show);
}
function show(e) {
e.stopPropagation();
this.classList.toggle("myClass");
}
}
.sub-menu ul ul {
max-height: 0;
position: relative;
top: 0;
left: 0;
opacity: 0;
visibility: hidden;
transition: all 0.6s ease-out;
background: white;
}
#subb.myClass>ul {
max-height: 32em;
visibility: visible;
opacity: 1;
}
<nav id="nav" class="sub-menu open">
<ul class="list-unstyled">
<li id="subb">
<a class="link">Soluciones</a>
<ul>
<li id="subb">
<a class="link">Correo y herramientas colaborativas</a>
<ul>
<li><a class="link ilink" href="https://www.google.com/">Google Workspace</a></li>
<li><a class="link ilink href=" https://www.google.com/ "">Google for Education</a></li>
<li><a class="link ilink" href="https://www.google.com/">Microsoft 365</a></li>
</ul>
</li>
</ul>

Related

Active Navigation On Scroll anchor links

I've been trying to figure this out for hours. I currently have a fixed nav for my anchor website. I would like the menu link background color to change when scrolling through each section. Like this: https://codepen.io/dbilanoski/pen/LabpzG . When I scroll through each section I only see the hover background color not the active state color. If you have any tips or advice, please advise.
Thank you!
var navLink = $(".nav-link"),
topMenuHeight = navLink.outerHeight() + 15,
//All list items
menuItems = navLink.find("a"),
//Anchors corresponding to menu items
scrollItems = menuItems.map(function() {
var item = $($(this).attr("href"));
if (item.length) {
return item;
}
});
// Bind click handler to menu items
// so we can get a fancy scroll animation
menuItems.click(function(e) {
var href = $(this).attr("href")
offsetTop = href === "#" ? 0 : $(href).offset().top - topMenuHeight + 1;
$('html, body').stop().animate({
scrollTop: offsetTop
}, 300);
e.preventDefault();
});
// Bind to scroll
$(window).scroll(function() {
// Get container scroll position
var fromTop = $(this).scrollTop() + topMenuHeight;
// Get id of current scroll item
var cur = scrollItems.map(function() {
if ($(this).offset().top < fromTop)
return this;
});
// Get the id of the current element
cur = cur[cur.length - 1];
var id = cur && cur.length ? cur[0].id : "";
if (navLink !== id) {
navLink = id;
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href='#" + id + "']").parent().addClass("active");
}
});
a {
color: inherit;
text-decoration: none;
}
a:hover {
color: #fa448c;
text-decoration: underline;
}
a:active {
background-color: #fa448c;
color: #fff;
}
li {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="menu">
<ul class="nav-list">
<li class="nav-item">
Home
</li>
<li class="nav-item">
Products
</li>
<li class="nav-item">
Featured
</li>
<li class="nav-item">
Best Sellers
</li>
<li class="nav-item">
Contact
</li>
</ul>
</div>
The trick of the codepen you've sent is that all sections have the same height. However, there's a more flexible way of doing that. Using this solution, you check for each section whether it's shown on the screen or not, and if it is, highlight its button in the navlist.

Change text color of a span with Javascript while hovering on seperated div

I have a menupoint and underneath it a seperate div / mega menu. I triggered the mega menu to show up via Javascript. When I am hovering over the mega menu, the desired span in the menu should get highlighted with another color and also the background color should change. You can see in the code how I tried to solve it (comments included). Can you please help me. I don´t know why I can´t trigger it via .getElementsByClassName!?
//Showing mega menu on hover over menu point
document.getElementById("menu-item-136").addEventListener("mouseover", mouseOver);
document.getElementById("menu-item-136").addEventListener("mouseout", mouseOut);
function mouseOver() {
document.getElementById("mega-menu").style.display = "block";
}
function mouseOut() {
document.getElementById("mega-menu").style.display = "none";
}
//Let mega menu stay visible when hovering over it
//Style menupoint when hovering over mega menu div (NOT WORKING)!
document.getElementById("mega-menu").addEventListener("mouseover", mouseOver);
document.getElementById("mega-menu").addEventListener("mouseout", mouseOut);
function mouseOver() {
document.getElementById("mega-menu").style.display = "block";
document.getElementsByClassName (".aux-menu-label").style.color = "yellow";
}
function mouseOut() {
document.getElementById("mega-menu").style.display = "none";
document.getElementsByClassName (".aux-menu-label").style.color = "";
}
.menu-item-136 {
background-color: grey;
height: 50px;
}
.menu-item-136:hover {
background-color:green;
}
.aux-menu-label {
color:blue;
}
.mega-menu-1 {
display: none;
background-color: green;
height: 200px;
}
<div>
<li id="menu-item-136" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-136 aux-menu-depth-0 aux-menu-root-2 aux-menu-item">
<a href="#" class="aux-item-content">
<span class="aux-menu-label"><i aria-hidden="true" class="services auxicon-list"></i> Leistungen</span>
<span class="aux-submenu-indicator"></span></a>
</div>
<div id="mega-menu" class="mega-menu-1">content</div>
Thanks for your help!
Your code is a bit messy but you are calling your class incorrectly:
This:
document.getElementsByClassName (".aux-menu-label")
Should be this:
document.getElementsByClassName ("aux-menu-label")
Additionally, when using getElementsByClassName you are provided with an array-like object with all elements that have the class you have specified.
With that in mind, you must run a loop to target elements with the styles you want to apply.
The below code is how we will target multiple labels and change them to yellow on hover:
var labels = document.getElementsByClassName("aux-menu-label");
for (var i = 0; i < labels.length; i++) {
labels[i].style.color = "yellow"
}
When you run the snippet below you will see I have used similar code to revert the color back to blue onmouseout.
Learn more about getElementsByClassName here.
//Including this to show you how to target CSS child elements as described in your comment
var childElement = document.querySelector('#menu-item-136 .aux-item-content');
childElement.style.backgroundColor = "white";
console.log(childElement);
//Showing mega menu on hover over menu point
document.getElementById("menu-item-136").addEventListener("mouseover", mouseOver);
document.getElementById("menu-item-136").addEventListener("mouseout", mouseOut);
function mouseOver() {
document.getElementById("mega-menu").style.display = "block";
}
function mouseOut() {
document.getElementById("mega-menu").style.display = "none";
}
//Let mega menu stay visible when hovering over it
//Style menupoint when hovering over mega menu div (NOT WORKING)!
document.getElementById("mega-menu").addEventListener("mouseover", mouseOver);
document.getElementById("mega-menu").addEventListener("mouseout", mouseOut);
function mouseOver() {
document.getElementById("mega-menu").style.display = "block";
var labels = document.getElementsByClassName("aux-menu-label");
for (var i = 0; i < labels.length; i++) {
labels[0].style.color = "yellow"
}
}
function mouseOut() {
document.getElementById("mega-menu").style.display = "none";
var labels = document.getElementsByClassName("aux-menu-label");
for (var i = 0; i < labels.length; i++) {
labels[i].style.color = "blue"
}
}
.menu-item-136 {
background-color: grey;
height: 50px;
}
.menu-item-136:hover {
background-color: green;
}
.aux-menu-label {
color: blue;
}
.mega-menu-1 {
display: none;
background-color: green;
height: 200px;
}
<div>
<li id="menu-item-136" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-136 aux-menu-depth-0 aux-menu-root-2 aux-menu-item">
<a href="#" class="aux-item-content">
<span class="aux-menu-label"><i aria-hidden="true" class="services auxicon-list"></i> Leistungen</span>
<span class="aux-submenu-indicator"></span></a>
</div>
<div id="mega-menu" class="mega-menu-1">content</div>
<div>
<li id="menu-item-136" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-136 aux-menu-depth-0 aux-menu-root-2 aux-menu-item">
<a href="#" class="aux-item-content">
<span class="aux-menu-label"><i aria-hidden="true" class="services auxicon-list"></i> Leistungen</span>
<span class="aux-submenu-indicator"></span></a>
</div>
<div id="mega-menu" class="mega-menu-1">content</div>
EDIT: I've included the following javascript to show you how to target child elements and apply CSS to them. The code below will target the child of #menu-item-136 and change its background color to white. Run the snippet to see.
var childElement = document.querySelector('#menu-item-136 .aux-item-content');
childElement.style.backgroundColor = "white";
console.log(childElement);
In your code
If we add some margin to megamenu wrapper This will be not working
and menu close when pointer out from menu item.
I have fixed that isseue
Its Looks like with WordPress menu. Please check below example and its will be helpful to use multiple mega-menues You need to map megamenu data id with menu class item.
codepen example
[].forEach.call(document.querySelectorAll('nav > ul > li'), function (link) {
link.addEventListener('mouseover', coloringHandler);
link.addEventListener('mouseout', decoloringHandler);
});
[].forEach.call(document.querySelectorAll('.megamenu'), function (menu) {
menu.addEventListener('mouseover', megamenuHover );
});
var state = false;
function coloringHandler(){
state = false;
hideAllMegamenu();
// add class to current hover element
this.classList.add('active');
var Classes = this.classList; // getting all class list
Classes.forEach(name => {
var megaMenu = document.querySelectorAll('[data-id="'+name+'"]'); // check if have any mached elements with class name
if(megaMenu.length == 1 ){
megaMenu[0].classList.add('active');
state = true;
megaMenu[0].addEventListener('mouseover', megamenuHover );
megaMenu[0].addEventListener('mouseout', megamenuHoverOut );
return;
}
});
}
function decoloringHandler(){
if( state == false ){
this.classList.remove('active');
hideAllMegamenu();
}
}
function hideAllMegamenu(){
// change elemets as you want
[].forEach.call(document.querySelectorAll('nav > ul > li'), function (li) {
li.classList.remove("active");
});
// .megamenu is common class
[].forEach.call(document.querySelectorAll('.megamenu'), function (menues) {
menues.classList.remove('active');
})
}
function megamenuHover() {
this.classList.add('in-hover');
}
function megamenuHoverOut() {
hideAllMegamenu();
}
nav ul{
display:flex;
list-style:none;
}
li{
margin:0px 10px;
}
a{
background:green;
display:block;
color:white;
padding:10px 20px;
}
ul li.active a{
background:red;
}
.megamenu{
background: red;
height:200px;
pointer-events: none;
opacity:0;
position:absolute;
width:100%;
padding:20px;
color:#fff;
transition:all .5s ease;
transform:translateY(50px);
}
.megamenu.active{
opacity:1;
pointer-events: all;
transform:translateY(0px);
}
<h1>Hover over the menu Items</h1>
<nav>
<ul>
<li class="menu-item-136">
<span>Home</span>
</li>
<li class="menu-item-137">
<span>Contact us</span>
</li>
<li class="menu-item-138">
<span>Danushka</span>
</li>
<li class="menu-item-139">
<span>About us</span>
</li>
</ul>
</nav>
<div class="megamenu" data-id="menu-item-137">
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</div>
<div class="megamenu" data-id="menu-item-138">
Danushka Megamenu... Add data id for mapping
</div>

How to make navigation menu disappear when clicking anywhere on the screen with HTML, CSS, and javascript

This webpage has a navigation menu that is functioning properly. When the navigation icon is clicked, the menu will appear and the icon will be replaced with an "X" icon. When the "X" is clicked the navigation menu disappears.
However, it would nice to make it so that while the navigation menu is present, clicking anywhere outside of it will make it disappear as well.
Here is a picture for context:
HMTL
<html>
<div id="sideNav">
<nav>
<ul>
<li>HOME</li>
<li>COVID-19</li>
<li>SERVICES</li>
<li>REVIEWS</li>
<li>CONTACT</li>
</ul>
</nav>
</div>
<div id="menuBtn">
<img src="images/menu.PNG" id="menu">
</div>
</html>
CSS
#sideNav{
width: 200px;
height: 100vh;
position: fixed;
right: -250px;
top:0;
background: #009688;
z-index: 2;
transition: 0.33s;
}
javascript
var menuBtn = document.getElementById("menuBtn")
var sideNav = document.getElementById("sideNav")
var menu = document.getElementById("menu")
sideNav.style.right = "-250px";
menuBtn.onclick = function(){
if(sideNav.style.right == "-250px"){
sideNav.style.right = "0";
menu.src = "images/close.PNG";
}
else{
sideNav.style.right = "-250px";
menu.src = "images/menu.PNG";
}
}
use event.stopPropagation() and add click event to the document
var menuBtn = document.getElementById("menuBtn")
var sideNav = document.getElementById("sideNav")
var menu = document.getElementById("menu")
menuBtn.onclick = function(e) {
//stop propagation of document click
e.stopPropagation()
//toggle side nav
if (!sideNav.classList.contains("open")) {
sideNav.classList.add("open");
menu.src = "images/close.PNG";
} else {
sideNav.classList.remove("open");
menu.src = "images/menu.PNG";
}
}
//stop propagation on the side nav element
sideNav.onclick = function(e) {
e.stopPropagation()
}
//close menu when document is clicked
document.onclick = function() {
sideNav.classList.remove("open");
menu.src = "images/menu.PNG";
}
#sideNav {
width: 200px;
height: 100vh;
position: fixed;
right: -250px;
top: 0;
background: #009688;
z-index: 2;
transition: 0.33s;
}
/*set the right to 0 for class open*/
#sideNav.open {
right: 0;
}
<html>
<div id="sideNav">
<nav>
<ul>
<li>HOME</li>
<li>COVID-19</li>
<li>SERVICES</li>
<li>REVIEWS</li>
<li>CONTACT</li>
</ul>
</nav>
</div>
<div id="menuBtn">
<img src="images/menu.PNG" id="menu">
</div>
</html>
To able to click anywhere on the screen and have something, then you should listen for clicks anywhere on the page. Attach an event listener to the document where you check if the clicked element is not a child of the side nav element.
You can check this with the closest() method which is like a querySelector() that runs up the DOM and evaluates each parent.
If there is no hit, then it means that you're not clicking inside the side navigation, so it must be outside the side navigation. Now you know that you can close the menu.
document.addEventListener('click', event => {
// If the clicked element is not a child of #sideNav..
if (event.target.closest('#sideNav') === null) {
// ..then close navigation..
// { your code here }
}
});
With jQuery:
$(document).mouseup(function (e){
let sideNav = $("#sideNav");
if (!sideNav.is(e.target) && sideNav.has(e.target).length === 0) {
sideNav.addClass("hide");
$("#menu").removeClass("close");
}
});
Pure js:
window.addEventListener('mouseup', function(e){
var sideNav = document.getElementById("sideNav");
var menu = document.getElementById("menu")
if (e.target.id != 'moreDrop') {
sideNav.classList.add('hide');
}
});

closing dropdown by clicking outside

I want to be able to close my dropdown menu not only by clicking the x, but by clicking outside of it aswell. My js code doesnt seem to work. The Javascript is copied from a template i had left somewhere but im actually not able to fix it in order for it to work.
window.onclick = function closeMenu() {
if(document.getElementById("dropdown-content").style.left != "-300px") {
var dropdown = document.getElementById("dropdown-content");
var i;
for(i = 0; i < dropdown.length; i++) {
var openDropdown = dropdown[i];
if(openDropdown.style.left != ('-300px')) {
openDropdown.style.left = ('-300px');
}
}
}
}
.dropdown-content {
position: fixed;
background-color: rgb(255, 255, 255);
width: 300px;
height: 100%;
/*box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);*/
z-index: 600;
transition: 0.3s;
}
#dropdown-content {
left: -300px;
z-index: 600;
}
<div class="dropdown-container">
<div class="dropdown-content" id="dropdown-content">
<div class="menubutton" onclick="menu(this)">
<div class="bar1"></div>
<div class="bar2"></div>
<div class="bar3"></div>
</div>
<div class="menulist">
Angebot des Tages
Alle Angebote
Technik
Hardware
Mode
Automobil
</div>
</div>
</div>
const x = document.querySelector('.x');
const ul = document.querySelector('ul');
x.addEventListener('click', () => {
ul.classList.toggle('show');
});
document.addEventListener('click', ({
target
}) => {
if (target.matches('.x') === false) {
ul.classList.remove('show');
}
});
ul {
display: none;
}
ul.show {
display: block;
}
<div class="x">X</div>
<ul>
<li>Test</li>
<li>Test</li>
<li>Test</li>
<li>Test</li>
</ul>
Here, we track the X and just use toggle(), for any other click we ensure it is not X and then just remove() our show class.
If you are using just vanilla javascript, you can add a eventListener to the document object that listens to click event and checking some condition of the dropdown to check if it's opened, if it is, then closes it, if it's not, do nothing.
Something like:
document.addEventListener('click', () => {
const dropdown = ... // grab dropdown element
if (isOpened(dropdown)) closeDropdown()
})
EDIT: Also you should check if the click happened outside your dropdown since it will also be triggered if it is clicked. For that you can use the Node API.
Leaving it as:
document.addEventListener('click', (e) => {
const dropdown = ... // grab dropdown element
if (dropdown.contains(e.target)) return; // if the clicked element is inside the dropdown or it's the dropdown itself, do nothing
if (isOpened(dropdown)) closeDropdown()
})

JS Dropdown Menu Behavior

I have a dropdown menu working but I can't figure out how to close the previous menu onclick. All the menues stay open so I need them to close when a different menu is open.
Please see my jsfiddle
https://jsfiddle.net/yvhnphp4/
$(document).ready(function(){
// Dropdown menu
var findDropdowns = document.querySelectorAll(".has-dropdown");
for(var i = 0; i < findDropdowns.length; i++) {
if(i == 0) {
var dropdownId = "has-dropdown-1";
findDropdowns[i].setAttribute("id", dropdownId);
}else {
var addOneToIndex = i + 1;
dropdownId = "has-dropdown-" + addOneToIndex;
findDropdowns[i].setAttribute("id", dropdownId);
}
var targetDropdown = document.getElementById(dropdownId);
targetDropdown.onclick = dropdownTrigger;
}
function dropdownTrigger(e) {
e.preventDefault();
var showHideDropdown = e.target.nextElementSibling;
showHideDropdown.setAttribute("class", "show");
}
});
<ul class="nav">
<li><a class="has-dropdown" href="">Link</a>
<ul>
<li>Sub-Link</li>
<li>Sub-Link</li>
<li>Sub-Link</li>
</ul>
</li>
<li><a class="has-dropdown" href="">Link</a>
<ul>
<li>Sub-Link</li>
<li>Sub-Link</li>
<li>Sub-Link</li>
</ul>
</li>
</ul>
.nav ul {display:none;}
.nav ul.show{display:block;}
You can simply remove the .show class from all ul tags in .nav that still have it, before opening a new dropdown:
function dropdownTrigger(e) {
var opened = document.querySelectorAll(".nav ul.show");
for(var i = 0; i < opened.length; i++) {
opened[i].removeAttribute("class");
}
...
}
Note that since you're using jQuery anyway ($(document).ready) there is probably a much better way to do this.
Also, use href="#" instead of href="".

Categories