JS logic: Open / close menu on click or replace with another - javascript

I have a menu which opens up a submenu on click.
If a link has a dropdown menu (has class header--has-submenu), then on click, I'm making another element (.header__subContainer) appear.
The issue I'm having is with the logic. For example, in my demo, I have two links, what we deliver and about us, both have submenus.
I want a user to be able to click onto a link to open it and then click on the same link to close it (standard UX).
However, I also want the user to be able to click on one link (let's say 'what we deliver) and then click onto about usto show that menus dropdown (then the user can click theabout us` link again to close the menu).
I've tried toggleClass but this causes issues with the last scenario above.
Here's a demo:
$(".header--has-submenu a.header__parentLink").click(function(e) {
e.preventDefault();
console.log("click");
var id = $(this).attr('data-menu');
// add active state styles to link to showcase which menu is open
$("li.header--has-submenu").removeClass("header--has-submenu--active");
$(this).parent().addClass("header--has-submenu--active");
// open subContainer (black div that contains all submenus)
if ( $(".header__subContainer-menu").hasClass("header__subContainer-menu--active") ){
$(".header__subContainer").removeClass("header__subContainer--active");
} else {
$(".header__subContainer").addClass("header__subContainer--active");
}
// remove active class from submenu, so only one menu is open at one time
$(".header__subContainer-menu").removeClass("header__subContainer-menu--active");
// open the relevant menu
$(this).closest(".header__subContainer").addClass("header__subContainer--active");
$(".header__subContainer-menu[data-menu='"+id+"']").toggleClass("header__subContainer-menu--active");
});
.header {
padding: 30px 0;
}
.header__parentUl * {
color: #FFFFFF;
}
.header__li {
margin: 0 20px;
}
.header__subContainer {
display: none;
}
.header__subContainer--active {
display: block;
}
.header__subContainer-menu {
display: none;
}
.header__subContainer-menu--active {
display: block;
}
.header--has-submenu--active a {
color: green;
}
.background--black {
background: #000000;
}
.reset-list {
list-style-type: none;
margin: 0;
padding: 0;
}
.color--white {
color: #FFFFFF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<header class="header background--black">
<div class="container">
<div class="col-xl-9 justify-content-end">
<nav class="header__menu">
<ul class="header__parentUl d-flex reset-list">
<li class="header__li header--has-submenu">
<a class="header__parentLink" href="#" data-menu="what-we-deliver">What we deliver</a>
</li>
<li class="header__li header--has-submenu">
<a class="header__parentLink" href="#" data-menu="about-us">About us</a>
</li>
</ul>
</nav>
</div>
</div>
<div class="container-fluid background--black header__subContainer">
<div class="row justify-content-center">
<div class="col-12">
<nav class="header__subContainer-menu" data-menu="what-we-deliver">
<ul class="header__subContainer-ul d-flex reset-list">
<li class="header__subContainer-li">
<a class="header__subContainer-link color--white " href="#">Link 1</a>
</li>
<li class="header__subContainer-li">
<a class="header__subContainer-link color--white " href="#">Link 2</a>
</li>
</ul>
</nav>
<nav class="header__subContainer-menu" data-menu="about-us">
<ul class="header__subContainer-ul d-flex reset-list">
<li class="header__subContainer-li">
<a class="header__subContainer-link color--white " href="#">Link 3</a>
</li>
<li class="header__subContainer-li">
<a class="header__subContainer-link color--white " href="#">Link 4</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</header>

Something like this should work:
$(".header--has-submenu a.header__parentLink").click(function(e) {
e.preventDefault();
let id = $(this).attr('data-menu');
// Check if link already active
let wasOpen = $(this).parent().hasClass("header--has-submenu--active");
// remove all active classes
$(".header--has-submenu").removeClass("header--has-submenu--active");
$(".header__subContainer-menu").removeClass("header__subContainer-menu--active");
$(".header__subContainer").removeClass("header__subContainer--active");
if(!wasOpen){
// the clicked link was not open
// add active classes to the 3 elements
$(this).parent().addClass("header--has-submenu--active");
$('.header__subContainer').addClass('header__subContainer--active');
$(".header__subContainer-menu[data-menu='"+id+"']").addClass("header__subContainer-menu--active");
}
});

Related

Change color of current selected element sidebar

I have a sidebar with site pages. I want to change the color of a currently selected page when clicked, assign the class is-active to the currently selected item, and remove it from all the others.
Here is the HTML:
<aside id="side-bar" class="sidebar">
<h3>Pages</h3>
<nav class="menu">
<a onclick="changeColor()" class="menu-item is-active">page 1</a>
<a onclick="changeColor()" class="menu-item">page 2</a>
<a onclick="changeColor()" class="menu-item">page 3</a>
</nav>
</aside>
And CSS:
.sidebar .menu .menu-item:hover,
.sidebar .menu .menu-item.is-active {
color: #f1672c;
border-right: 6px solid #f1672c;
}
Each element has the function change color that should take all the elements, remove the is-active class from previous selected element and add to the curent clicked element.
function changeColor(){
var links = document.getElementsByClassName("menu-item")
links.map(classList.remove("is-active"))
this.classList.add("is-active")
}
What am I missing?
That changeColor() is not necessary....
const pages = document.querySelectorAll(".menu-item");
pages.forEach((item) => {
item.addEventListener('click', active_item);
})
function active_item () {
pages.forEach((item) => {
item.classList.remove('is-active');
});
this.classList.add('is-active');
}
.is-active {
background-color: red;
}
<aside id="side-bar" class="sidebar">
<h3>Pages</h3>
<nav class="menu">
<a class="menu-item is-active">page 1</a>
<a class="menu-item">page 2</a>
<a class="menu-item">page 3</a>
</nav>
</aside>
Just pass the reference of current element with this.
function changeColor(el){
document.querySelector(".menu-item.is-active").classList.remove("is-active")
el.classList.add("is-active")
}
.sidebar .menu .menu-item:hover,
.sidebar .menu .menu-item.is-active {
color: #f1672c;
border-right: 6px solid #f1672c;
}
<aside id="side-bar" class="sidebar">
<h3>Pages</h3>
<nav class="menu">
<a onclick="changeColor(this)" class="menu-item is-active">page 1</a>
<a onclick="changeColor(this)" class="menu-item">page 2</a>
<a onclick="changeColor(this)" class="menu-item">page 3</a>
</nav>
</aside>

Toggle collapse menu is not working and hiding the content

I'm working on this website - https://sampledemos.online/training/dashboard.html in which the toggle menu is hiding the content when menu expands.
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme active">
<div class="app-brand demo">
<a href="index.html" class="app-brand-link">
<img class="logo" src="/training/images/weblogo.jpg">
</a>
<a href="javascript:void(0);" class="layout-menu-toggle menu-link text-large ms-auto">
<i class="bx bx-chevron-left bx-sm align-middle" onclick="openNav()"></i>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<!-- Dashboard -->
<li class="menu-item active">
<a href="course-master-list.html" class="menu-link">
<i class="menu-icon tf-icons bx bxs-book-reader" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Course Master"></i>
<div data-i18n="course">Course Master</div>
</a>
</li>
and am using javascript as below
<script>
function openNav() {
$(document).ready(function () {
$('#layout-menu').toggleClass('active');
});
}
</script>
Below I have used styles like this...
#layout-menu.active {
width: 64px;
}
.active~.layout-page {
padding-left: 65px !important;
}
.app-brand .layout-menu-toggle {
position: absolute;
left: 15rem;
border-radius: 50%;
}
.active .layout-menu-toggle {
left: 4rem;
}
```
Is there I missed anything in my codings...
If I understand your question correctly then what's happening is that your plugin 'perfect scrollbar' is setting adding the class 'ps' to <ul class="menu-inner py-1 ps"> which adds overflow: hidden !important. That means everything that falls outside that menu-inner container is hidden. If you check your element inspector and remove the overflow: hidden you'll see your toggle menu becomes visible.

Auto collapse sidebar menu list when lost focus

I just want to ask :
how to make the dropdown-menu collpase when the dropdown menu is active?
When the mouse hovers to the sidebar, the active dropdown menu expanded again?
i think i just wondering using hover but it doesnt work when i try it, so i hope someone can help me to solve this?
Simple Concept I Just want :
Mouse:hover to sidebar-icon/burger is clicked => sidebar-expanded => menu list that have:submenu clicked => dropdown-menu displayed => mouseOut from sidebar => icon is-collapsed
Mouse:hover to sidebar-icon again / sidebar:state(active) => dropdown menu is already expanded
well i dont know that you can understand my question or something.. but i hope u can make this auto collapse menu
$(document).ready(function() {
// Sidebar links
$('.sidebar .side-list li a').on('click', function() {
const $this = $(this);
if ($this.parent().hasClass('buka')) {
$this
.parent()
.children('.dropdown-menu')
.slideUp(200, () => {
$this.parent().removeClass('buka');
});
} else {
$this
.parent()
.parent()
.children('li.buka')
.children('.dropdown-menu')
.slideUp(200);
$this
.parent()
.parent()
.children('li.buka')
.children('a')
.removeClass('buka');
$this
.parent()
.parent()
.children('li.buka')
.removeClass('buka');
$this
.parent()
.children('.dropdown-menu')
.slideDown(200, () => {
$this.parent().addClass('buka');
});
}
});
// ٍSidebar Toggle
$('.burger').on('click', e => {
$('.konten').toggleClass('k-kebuka');
$('.sidebar').toggleClass('s-kebuka');
e.preventDefault();
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="sidebar" class="sidebar bg-cerah">
<div class="sidebar-dalem">
<div class="side-konten">
<ul class="side-list">
<li class="side-item dropdown">
<a class="dropdown-toggle" href="javascript:void(0)">
<span class="side-ikon">ICON</span>
<span class="side-judul">MENU</span>
<span class="panah">[>]</span>
</a>
<ul class="dropdown-menu">
<li>
SUBMENU #1
</li>
<li>
SUBMENU #2
</li>
</ul>
</li>
<li class="side-item dropdown">
<a class="dropdown-toggle" href="javascript:void(0)">
<span class="side-ikon">[X]</span>
<span class="side-judul">MENU #2</span>
<span class="panah">[>] ></span>
</a>
<ul class="dropdown-menu">
<li>
SUBMENU #1
</li>
<li>
SUBMENU #2
</li>
</ul>
</li>
<li class="side-item">
<a class="side-link" href="javascript:void(0)">
<span class="side-ikon">[X]</span>
<span class="side-judul">MENU #3</span>
</a>
</li>
</ul>
</div>
</div>
</div>
<div id="konten" class="konten">
<div class="konten-navbar sticky-top">
<ul class="navbar-kiri">
<li>
<a id="burger" href="javascript:void(0)" class="burger">BURGER ICO</a>
</li>
<li>
SEARCH ICO
</li>
</ul>
</div>
</div>
In pure CSS, .menu:hover definitely works, but you have to build the entire menu inside of the element that has the :hover pseudo class and also either touching or overlapping:
.popup {
position: relative;
}
button {
width: 8em;
height: 40px;
}
ul {
position: absolute;
top: 36px;
left: 8px;
background: #eee;
border: 1px solid black;
display: none;
margin: 0;
width: 14em;
min-height: 8em;
padding: 0.5em;
list-style: none;
}
.popup:hover ul {
display: block;
}
<div>
<div class="popup"><button>Menu</button>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
</div>
Certainly, you'd need to style everything properly.
I also seem to remember that JQuery has OnMouseEnter and OnMouseLeave events that work kind of like :hover too.

How to add active class to list and parent list in drop down

Please help me to resolve this problem.
Below is my navigation bar. I used Bootstrap 3 for navigation bar.
How to add active class to current list anchor tag and also add active class to parent list anchor tag in drop down menu.
Below is my html, css, and js file.
please guide me to resolve my problem.
//dropdown menu
$('.nav_bar_custom .drop_menu1').hover(function() {
$(this).children('ul').stop(true, false, true).slideToggle(300);
});
var path = window.location.pathname.split("/").pop();
if ( path == '' ) {
path = 'index.php';
}
var target = $('nav .nav_bar_custom li a[href="'+path+'"]');
target.addClass('active_menu');
.navigation{
background: #304269;
}
.nav li a{ color: #fff;}
nav li{
display: inline-block;
float: left;
}
.nav li .drop_menu2 li{
display: block;
float: none;
}
nav ul li a.active_menu {
color: #0BADEE;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<div class="navigation clearfix" id="navigation">
<nav>
<ul class="nav navbar-nav navbar-right nav_bar_custom">
<li>
Home
</li>
<li class="drop_menu1">
Training
<ul class="drop_menu2" id="drop_menuT" style="display: none;">
<li>
Basic
</li>
<li>
Intermediate
</li>
<li>
Advance
</li>
</ul>
</li>
<li>
Blog
</li>
<li>
Contact
</li>
</ul>
</nav>
</div>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Please use below script:
<script>
var path = 'basic'; //Dynamic value using PHP
var target = $("a[href$='"+path+"']");
target.addClass('active_menu');
target.parent().parent().parent().find('a:first').addClass('active_menu');
</script>

Targeting data-state to toggle nav headers

UPDATE
I discovered I could toggle the data state on click with
$(".list-header").attr("data-state", $(".list-header").hasClass('list-header') ? "hide" : "show");
However, when I click the same header I just opened, it doesn't revert back to its original icon or "hide" state.
How can I toggle the current header back to the data-state="hide" state?
$('.list-header.major').click(function(){
$(".list-header").attr("data-state", $(".list-header").hasClass('list-header') ? "hide" : "show");
$('.list-header.major').next('div').slideUp();
$(this).next('div').toggle();
$('.list-header.major').removeClass('active');
$(this).toggleClass('active');
return false;
});
===========================================================================
I need to be able to toggle the "HEADER"'s so that when the subnav is closed for any of them, the header icon is always set to a + sign, and has a data-state=hide
I found that the state of the headers is always set to active, and that the only way to toggle the + or - sign is to toggle the data-state from show to hide, which would be based on the state of the .col-wrapper classes style being either display:none or display: block.
The question is how to look for the display state of the .col-wrapper class and change the data-state based on whether the .list-header has been clicked or not. I've included screen shots of the states from the fiddle(https://jsfiddle.net/2bu35uLn/3/show/) below.
Essentially, the issue is that the icons aren't toggling back to the plus sign, if the user clicks on another header. This should force any closed list header to go back to the plus sign.
This is the closed state
This is the open state
Notice that the .col-wrapper class has display:block to show the subnav, but the .list-header class is still set to data-state="hide", which keeps the header from being toggled back to the correct state.
I'm trying to target the data-state to change to hide if the .col-wrapper class is display:none, by doing something like this:
$(".list-header").on('click', function(){
if($(this).find(".col-wrapper").css("display", "none")){
force data-state to be hide
}
});
HTML
<div class="inner">
<button data-target="#data-nav-0" data-toggle="collapse" data-state="show" class="list-header major no-style"> <span>HEADER</span> </button>
<div class="col-wrapper collapse in" id="data-nav-0" style="display: block;">
<div class="column">
<ul class="list-unstyled">
<li class="list-header mobile">link</li>
<li class="list-header desktop">head</li>
<li class="mobile chevron-right" data-target="#data-inner-0-0" data-toggle="collapse" data-state="hide">
<button class="no-style sub-header">This should toggle the ul links</button>
</li>
<ul class="inner collapse" id="data-inner-0-0">
<li>link</li>
<li>link</li>
<li>link</li>
<li>link</li>
</ul>
</ul>
</div>
<div class="column">
<ul class="list-unstyled">
<li class="list-header desktop">Link</li>
<li class="mobile chevron-right" data-target="#data-inner-0-1" data-toggle="collapse" data-state="hide">
<button class="no-style sub-header">This should toggle the ul links</button>
</li>
<ul class="inner collapse" id="data-inner-0-0">
<li>link</li>
<li>link</li>
<li>link</li>
<li>link</li>
</ul>
</ul>
</ul>
</div>
</div>
</div>
<div class="inner">
<button data-target="#data-nav-0" data-toggle="collapse" data-state="show" class="list-header major no-style"> <span>HEADER</span> </button>
<div class="col-wrapper collapse in" id="data-nav-0" style="display: block;">
<div class="column">
<ul class="list-unstyled">
<li class="list-header mobile">link</li>
<li class="list-header desktop">head</li>
<li class="mobile chevron-right" data-target="#data-inner-0-0" data-toggle="collapse" data-state="hide">
<button class="no-style sub-header">This should toggle the ul links</button>
</li>
<ul class="inner collapse" id="data-inner-0-0">
<li>link</li>
<li>link</li>
<li>link</li>
<li>link</li>
</ul>
</ul>
</div>
<div class="column">
<ul class="list-unstyled">
<li class="list-header desktop">Link</li>
<li class="mobile chevron-right" data-target="#data-inner-0-1" data-toggle="collapse" data-state="hide">
<button class="no-style sub-header">This should toggle the ul links</button>
</li>
<ul class="inner collapse" id="data-inner-0-0">
<li>link</li>
<li>link</li>
<li>link</li>
<li>link</li>
</ul>
</ul>
</ul>
</div>
</div>
</div>
jQuery
$('.list-header.major').next('div').toggle();
$('.list-header.major').click(function(){
$('.list-header.major').next('div').slideUp();
$(this).next('div').toggle();
$('.list-header').removeClass('active');
$(this).toggleClass('active');
return false;
});
$('.mobile').next('ul').toggle();
$('.mobile').click(function() {
$('.mobile').next('ul').slideUp();
$(this).next('ul').toggle();
$('.mobile button').removeClass('active');
if ($(this).next('ul').is(':visible')) {
$(this).find('button').addClass('active');
}
return false;
});
CSS
button.list-header.major {
&:before,
&:after {
color: $purple;
font-family: 'Material Icons';
font-size: 20px;
position: absolute;
right: 20px;
}
&:before {
content: "\E145"; <--THIS IS A PLUS SIGN
}
&:after {
content: "\E15B"; <--THIS IS A MINUS SIGN
}
}
.sub-header:after{
content: " >";
font-family: 'Material Icons';
display: inline-block;
transform: rotate(0);
font-size: 26px;
color: $white;
position: relative;
top: 8px;
transition: transform .3s;
}
.sub-header.active:after{
content: " v";
color: red;
display: inline-block;
transform: rotate(8deg);
font-size: 26px;
color: $neon-green;
transition: transform .3s;
}
UPDATE WITH SCREENSHOT AND MORE DETAIL
The top navigation should have a + icon, not -. Here you can see the second Header is opened and has the correct -, while the Header above it still has the -, although the contents col-wrapper has collapsed below it:
Changed code together with the author of question. Should work as expected.
$('.list-header.major').click(function(){
if (current !== this)
$(current).attr('data-state', 'hide');
$(this).attr("data-state", $(this).attr('data-state') === 'hide' ? "hide" : "show");
current = this;
$('.list-header.major').next('div').slideUp();
$(this).next('div').toggle();
$('.list-header.major').removeClass('active');
$(this).toggleClass('active');
return false;
});
Here is it the fiddle

Categories

Resources