With the help of Michael M. I edited my WordPress menu code in the previous question nicely.
Now I want to open the submenu slowly whenever every li is clicked if there is a ul under it, which I wrote this code but in addition to opening the sub menu, I want the right direction sign (→) to the down direction sign (↓ ) to change.
I have put all the code of this professional menu below, but my problem is only related to the last script code.
let icon = document.querySelector(".icon_menu");
let nav = document.querySelector(".main_menu");
$('.back').hide();
$('.back').click(function() {
if ($(this).is(':hidden')) return;
$(this).toggle();
icon.classList = "bi bi-grid-fill icon_menu";
icon.style.left = "2%";
icon.style.color = "#a66fff";
icon.style.fontSize = "40px";
nav.style.left = '-300px';
});
icon.addEventListener("click", function() {
if (this.classList.contains("bi-grid-fill")) {
this.classList = "bi bi-x-circle-fill icon_menu";
icon.style.left = "21%";
icon.style.color = "#ff6f6f";
icon.style.fontSize = "30px";
nav.style.left = 0;
} else {
this.classList = "bi bi-grid-fill icon_menu";
icon.style.left = "2%";
icon.style.color = "#a66fff";
icon.style.fontSize = "40px";
nav.style.left = "-300px";
}
$('.back').toggle();
});
//********************** **A script that needs editing** *********************
$('.main_menu').find('li').click(function(sub_menu) {
sub_menu.stopPropagation();
sub_menu.preventDefault();
$(this).children('ul').slideToggle();
if ($(this).is(style.content = ' → ')) {
this.style.content = " ↓ ";
};
});
.main_menu {
position: absolute;
top: 0;
left: -300px;
bottom: 0;
z-index: 999;
background: #eee;
padding-right: 2rem;
transition: all 1s ease;
}
.icon_menu {
position: fixed;
top: 10%;
left: 2%;
font-size: 40px;
color: #a66fff;
cursor: pointer;
z-index: 99999;
transition: all 1.1s ease;
}
.main_menu ul {
list-style: none;
line-height: 60px;
font-size: 35px;
}
.main_menu ul li a {
color: #000000;
text-decoration: none;
padding-right: 15px;
padding-left: 40px;
margin-left: -60px;
margin-bottom: 10px;
}
.back {
width: 100%;
height: 100%;
position: fixed;
z-index: 9 !important;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #00000056;
}
.main_menu ul li ul {
display: none;
}
.main_menu ul li>a::after {
content: ' → ';
}
.main_menu ul li:first-child>a::after {
content: ' → ';
}
.main_menu ul li>a:only-child::after {
content: ' ';
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.10.3/font/bootstrap-icons.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<nav id="nav">
<span class="bi bi-grid-fill icon_menu"></span>
<aside class="main_menu">
<ul>
<li>
home
</li>
<li>
our articles
<ul>
<li>
social
</li>
<li>
Academic
</li>
<li>
historical
</li>
</ul>
</li>
<li>about us</li>
</ul>
</aside>
</nav>
</section>
<div class="back"></div>
It's probably best to do this with CSS instead of JavaScript. Just add the submenu class to any <li> elements that hold sub-menus. Then, create an open class that will be toggled from the JavaScript. From your click handler, all you need to do is toggle the open class.
Because you're using WordPress and can't give elements classes directly, you can use some JavaScript trickery to assign the submenu class to each li in the main_menu that has a ul child.
Like this:
let icon = document.querySelector(".icon_menu");
let nav = document.querySelector(".main_menu");
$('.back').hide();
$('.back').click(function() {
if ($(this).is(':hidden')) return;
$(this).toggle();
icon.classList = "bi bi-grid-fill icon_menu";
icon.style.left = "2%";
icon.style.color = "#a66fff";
icon.style.fontSize = "40px";
nav.style.left = '-300px';
});
icon.addEventListener("click", function() {
if (this.classList.contains("bi-grid-fill")) {
this.classList = "bi bi-x-circle-fill icon_menu";
icon.style.left = "21%";
icon.style.color = "#ff6f6f";
icon.style.fontSize = "30px";
nav.style.left = 0;
} else {
this.classList = "bi bi-grid-fill icon_menu";
icon.style.left = "2%";
icon.style.color = "#a66fff";
icon.style.fontSize = "40px";
nav.style.left = "-300px";
}
$('.back').toggle();
});
//********************** **A script that needs editing** *********************
// hack to give the 'submenu' class
$('.main_menu li ul').each(function() {
$(this).parent().addClass('submenu')
})
$('.submenu').click(function(sub_menu) {
sub_menu.stopPropagation();
sub_menu.preventDefault();
$(this).children('ul').slideToggle();
$(this).toggleClass('open');
});
.main_menu {
position: absolute;
top: 0;
left: -300px;
bottom: 0;
z-index: 999;
background: #eee;
padding-right: 2rem;
transition: all 1s ease;
}
.icon_menu {
position: fixed;
top: 10%;
left: 2%;
font-size: 40px;
color: #a66fff;
cursor: pointer;
z-index: 99999;
transition: all 1.1s ease;
}
.main_menu ul {
list-style: none;
line-height: 60px;
font-size: 35px;
}
.main_menu ul li a {
color: #000000;
text-decoration: none;
padding-right: 15px;
padding-left: 40px;
margin-left: -60px;
margin-bottom: 10px;
}
.back {
width: 100%;
height: 100%;
position: fixed;
z-index: 9 !important;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #00000056;
}
.main_menu ul li ul {
display: none;
}
.main_menu .submenu > a::after {
content: ' → ';
}
.main_menu .open > a::after {
content: ' ↓ ' !important;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.10.3/font/bootstrap-icons.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<nav id="nav">
<span class="bi bi-grid-fill icon_menu"></span>
<aside class="main_menu">
<ul>
<li>
home
</li>
<li>
our articles
<ul>
<li>
social
</li>
<li>
Academic
</li>
<li>
historical
</li>
</ul>
</li>
<li>about us</li>
</ul>
</aside>
</nav>
</section>
<div class="back"></div>
Related
The effect that I wanted is an active underline and change color. When I click on a button, the button will change color and have a underline.
.menu_item {
display: inline-block;
position: relative;
}
.menu_link {
font-family: "Montserrat";
font-size: 20px;
font-weight: 600;
color: grey;
letter-spacing: 0.6px;
}
.menu_link:hover,
.menu_link.active {
color: black;
}
.menu_link:after {
content: '';
position: absolute;
width: 125%;
transform: scaleX(0);
height: 3px;
bottom: -30px;
left: -.8rem;
background-color: black;
transform-origin: bottom right;
transition: transform 0.25s ease-out;
}
.menu_link:hover:after,
.menu_link.active:after {
transform: scaleX(1);
transform-origin: bottom left;
}
<ul class="menu p-0">
<li class="menu_item me-5">
<a class="text-decoration-none menu_link" type="button" id="btnContact">Contact Us</a>
</li>
<li class="menu_item">
<a class="text-decoration-none menu_link" type="button" id="btnLocation">Location</a>
</li>
</ul>
The Buttons
And I have tried with JS but I don't know how to add :after(underline effect) in onclick JS.
var btnMenu1 = document.getElementById("btnContact");
var btnMenu2 = document.getElementById("btnLocation");
btnMenu1.onclick = function() {
btnMenu1.style.color = "black";
btnMenu2.style.color = "grey";
}
btnMenu2.onclick = function() {
btnMenu1.style.color = "grey";
btnMenu2.style.color = "black";
}
//Any help or tutorials are appreciated, thank you!//
================================================================
closed, the solution:
var menuActive = '.menu_link';
$(menuActive).on('click', function(){
$(menuActive).removeClass('active');
$(this).addClass('active');
});
Working on a side nav that appears when I click on the burger menu. I am relatively new to this. I'm using event listeners to add and remove a class. But what's happening is the click is registered, the transition starts and then is cut off and doesn't continue, it goes back to original state.
const menuBtn = document.querySelector(".burgerMenu");
const sMenu = document.querySelector("#sideMenu");
const bI1 = document.querySelector(".burgerIcon1");
const bI2 = document.querySelector(".burgerIcon2");
const bI3 = document.querySelector(".burgerIcon3");
menuBtn.addEventListener('click', () => {
if (!sMenu.classList.contains('menuAway')) {
sMenu.classList.add('menuAway');
} else {
sMenu.classList.remove('menuAway');
}
if (!bI1.classList.contains('oneOnClick')) {
bI1.classList.add('oneOnClick');
} else {
bI1.classList.remove('oneOnClick');
}
if (!bI2.classList.contains('twoOnClick')) {
bI2.classList.add('twoOnClick');
} else {
bI2.classList.remove('twoOnClick');
}
if (!bI3.classList.contains('threeOnClick')) {
bI3.classList.add('threeOnClick');
} else {
bI3.classList.remove('threeOnClick');
}
})
.menu {
background: var(--gradient);
height: 100vh;
width: 38%;
position: fixed;
right: 0;
top: 0;
z-index: 3;
display: flex;
align-items: center;
padding-left: 5%;
opacity: 0.92;
transition: 0.3s;
}
.menuAway {
right: -75vw;
}
.menu li {
font-size: 1.8rem;
text-transform: uppercase;
font-weight: 500;
line-height: 4rem;
}
.menu li:hover {
text-decoration: underline;
}
.burgerMenu {
z-index: 4;
}
.burgerIcon {
width: 35px;
height: 3px;
margin: 5px auto;
background-color: #282828;
border-radius: 10px;
transition: 0.3s;
}
.oneOnClick {
transform: translate(0px, 8px) rotate(45deg);
}
.twoOnClick {
width: 0px;
}
.threeOnClick {
transform: translate(0px, -8px) rotate(-45deg);
}
<div class="navParent">
<div class="navBar">
<a href="index.html">
<img src="Assets/logo.svg" alt="Inform Logo" class="smallLogo">
</a>
<a href="" class="burgerMenu">
<div class="burgerIcon burgerIcon1"></div>
<div class="burgerIcon burgerIcon2"></div>
<div class="burgerIcon burgerIcon3"></div>
</a>
</div>
<section class="menu menuAway" id="sideMenu">
<ul>
<a href="about.html">
<li>About Us</li>
</a>
<a href="projects.html">
<li>Projects</li>
</a>
<a href="articles.html">
<li>Articles</li>
</a>
<a href="contact.html">
<li>Contact Us</li>
</a>
</ul>
</section>
</div>
This happens because of the <a href=""> so when you click the link you actually navigate, causing the page to reload. I suggest replacing the link with a button, or for a quick fix, do <a href="javascript:void(0)">
Also, you can make your code a lot easier to read by using classList.toggle instead of the if/else blocks
https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle
E.g. replace
if (!sMenu.classList.contains('menuAway')) {
sMenu.classList.add('menuAway');
} else {
sMenu.classList.remove('menuAway');
}
With
sMenu.classList.toggle('menuAway')
So I was trying to make a mobilenav menu, but what happens is when I click on my hamburger, it causes the list items to display as inline-block, but the function is set to display them as block level elements.
HTML
<div class="nav-options">
<i class="fas fa-bars" onclick="mobilenav()"></i>
<ul id="myLinks">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
CSS
#myLinks {
text-align: right;
}
#myLinks li {
list-style: none;
display: inline-block;
padding: 0.5rem 2rem;
font-size: 20px;
}
.nav-options i {
display: none;
}
#myLinks li:hover {
background-color: #ff3f05;
cursor: pointer;
transition: all 0.5s;
}
CSS for mobile view:
.nav-options i {
display: block;
font-size: 25px;
text-align: right;
}
.nav-options #myLinks {
display: none;
padding: 0px;
}
JS
function mobilenav() {
var links = document.getElementById("myLinks");
if (links.style.display === "block") {
links.style.display = "none";
} else {
links.style.display = "block";
}
}
While you are assigning the display property of the <li> elements, you are controlling the display property of the <ul> element. In the solution below I removed the display properties from the CSS file. The load event is fired when the page loads, and both the <ul> element and the <li> element are assigned a display style. Then when the <i> element is clicked, the click event occurs and the display property is toggled.
let icon = document.getElementById("icon");
var linkContainer = document.getElementById("myLinks"); /* <ul> element */
var linkElements = document.querySelectorAll('li'); /* <li> elements */
window.addEventListener('load', (event) => {
linkContainer.style.display = "block";
linkElements.forEach(li => {
li.style.display = "block";
});
});
function log() {
console.clear();
console.log(`<ul> display style: ${linkContainer.style.display}`);
console.log(`<li> display style: ${linkElements[0].style.display}`);
}
icon.addEventListener("click", function(e) {
log();
if(linkContainer.style.display === "block")
linkContainer.style.display = "none";
else
linkContainer.style.display = "block";
});
#myLinks {
text-align: right;
}
#myLinks li {
list-style: none;
/* display: inline-block; */
padding: 0.5rem 2rem;
font-size: 20px;
}
.nav-options i {
display: none;
}
#myLinks li:hover {
background-color: #ff3f05;
cursor: pointer;
transition: all 0.5s;
}
.nav-options i {
display: block;
font-size: 25px;
text-align: right;
}
.nav-options #myLinks {
display: none;
padding: 0px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div class="nav-options">
<i id="icon" class="fas fa-bars"></i>
<ul id="myLinks">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
The sidebar menu below works as expected but there are some elements that I would like to fix and improve.
#1 If you expand the 'System Admin' section the line that is displayed on the left side a in the correct place. However, if you
click on the 'Access Control' the line eon the left side is going
below the horizontal line that's pointing to that section. I tried
fixing that with css 'last-child' method but still is not working
correct. If I change percentage from 50% to 85% then the other
section will have an issue.
#2 I'm looking for a better way to expand/collapse the sections. The function show mimic the existing one, but instead of using the object
with key items I would like to use classes instead if possible.
$( document ).ready(function() {
SIDEBAR_OLD.BASE.ExpandCollapse('Auto');
});
const SIDEBAR_OLD = {};
SIDEBAR_OLD.BASE ={};
SIDEBAR_OLD.BASE.ToggleContent = function(section_id) {
let $sContents = $("#section_" + section_id);
if ( $sContents.css("display") != "none" ) {
$sContents.css("display","none");
} else { // Default to seeing the folder's contents:
$sContents.css("display","");
}
};
$("#main-page-wrapper").on("click", ".toggle-menu", function(e){
e.preventDefault();
let section_id = $(this).attr("data-id");
SIDEBAR_OLD.BASE.ToggleContent(section_id);
});
SIDEBAR_OLD.BASE.ExpandCollapse = function(action) {
let $sContents = null,
sExpand = null,
sRoot = false,
items = {1:"m",2:"sysadmin",5:"access"};
for (key in items) {
$sContents = $("#section_" + items[key]);
switch (action) {
case "Expand":
sExpand = "Yes";
break;
case "Collapse":
sExpand = "No";
break;
default:
if ( !(sExpand = $sContents.attr("data-expand")) ) sExpand = "Yes";
}
// Never close root elements automatically. Only ToggleContent(pNumber), below, can do that.
if ( sRoot = $sContents.attr("data-root") ) { // Note! Assignment! "=", not "=="!
if (sRoot == "Yes") sExpand = "Yes";
}
if ( sExpand == "No" ) {
$sContents.css("display","none");
} else { // Default to seeing the folder's contents:
$sContents.css("display","");
}
}
return true;
};
$("#main-page-wrapper").on("click", ".collapse-menu", function(e){
e.preventDefault();
let action = $(this).attr("data-action");
SIDEBAR_OLD.BASE.ExpandCollapse(action);
});
.sidebar {
position: absolute;
top: 56px;
right: 0px;
bottom: 0px;
left: 0px;
width: 180px;
background-color: #0071bc;
color: #fff;
height: calc(100vh - 98px);
overflow: auto;
font-size: 9pt !important;
white-space: nowrap;
}
.sidebar a {
color: #fff;
}
.menuitem {
color: #fff;
font-weight: bold;
text-decoration: none;
}
.menuitem:hover, .menuitem:focus {
color: #ff0;
}
#tree {
font-weight: bold;
padding: 1px;
}
#expandcollapse {
border: 1px solid white;
text-align: center;
}
.sb-row {
border: 0px;
height: 22px;
margin: 0px;
overflow: hidden;
padding: 0px 0px 0px 3px;
position: relative;
white-space: nowrap;
}
.fa-folder-open:before {
color: #DBDB2A !important;
}
.nav>li {
position: relative;
display: block;
}
.nav>li>a {
position: relative;
display: block;
padding: 7px 22px 6px;
}
.nav>li>a:focus {
text-decoration: none;
background: transparent;
background-color: transparent;
}
.nav > li > a:hover {
color: red;
background-color: transparent;
text-decoration: none;
}
.nav.side-menu>li {
position: relative;
display: block;
cursor: pointer;
}
.nav.child_menu li {
padding-left: 20px;
}
.nav.child_menu>li>a {
font-weight: 500;
font-size: 12px;
font-weight: bold;
}
li.first-level::before {
background: #fff;
bottom: auto;
content: "";
height: 1px;
left: 10px;
margin-top: 14px;
position: absolute;
right: auto;
width: 8px;
}
li.first-level::after {
border-left: 1px solid #fff;
bottom: 0;
content: "";
left: 10px;
position: absolute;
top: 0;
}
ul.nav.side-menu li.first-level:last-child::after {
bottom: 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<body>
<div class="container body" id="main-page-wrapper">
<div class="sidebar">
<div id="tree" role="navigation" data-expandall="Auto">
<div id="expandcollapse">
<a class="menuitem collapse-menu pr-2" href="#" data-action="Expand">Expand</a> | <a class="menuitem collapse-menu pl-2" href="#" data-action="Collapse">Collapse</a>
</div>
<div class="sb-row">
<a class="toggle-menu" title="Open/Close Folder - System Management" data-id="m">
<i class="fa fa-folder-open fa-lg"></i> System Management
</a>
</div>
<div id="section_m" class="menu-section" data-root="Yes" data-expand="Yes">
<ul class="nav side-menu">
<li class="first-level">
<a class="toggle-menu" href="#" title="System Admin" data-id="sysadmin"><i class="fa fa-folder-open"></i> System Admin</a>
<ul class="nav child_menu first" id="section_sysadmin" data-expand="No">
<li><a class="link-item" data-action="param" title="System Parameters">System Parameters</a></li>
<li><a class="link-item" data-action="schema" title="Select Schema">Select Schema</a></li>
<li><a class="link-item" data-action="item" title="Menu Item">Menu Items</a></li>
</ul>
</li>
<li class="first-level">
<a class="toggle-menu" href="#" title="Access Control" data-id="access"><i class="fa fa-folder-open"></i> Access Control</a>
<ul class="nav child_menu first" id="section_access" data-expand="No">
<li><a class="link-item" data-action="user" title="Manage User">Manage User</a></li>
<li><a class="link-item" data-action="role" title="Manage Role">Manage Role</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</body>
The script activates only the main menu items. How to make the code apply to sub-items. 2.1 - 2.2 And other subparagraphs if they exist. I can’t understand how to select all li elements including those embedded in each other.
Please help me finish the code, or maybe someone has a similar script with the same functionality.
var lastId,
topMenu = $("#top-menu"),
topMenuHeight = topMenu.outerHeight() + 15,
// All list items
menuItems = topMenu.find(".scroll-to"),
// Anchors corresponding to menu items
scrollItems = menuItems.map(function() {
var item = $($(this).attr("href"));
if (item.length) {
return item;
}
});
// 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 (lastId !== id) {
lastId = id;
// Set/remove active class
menuItems
.parent().removeClass("active")
.end().filter("[href='#" + id + "']").parent().addClass("active");
}
});
body {
height: 6000px;
font-family: Helvetica, Arial;
}
#top-menu {
position: fixed;
z-index: 1;
background: white;
left: 0;
right: 0;
top: 50px;
}
#top-menu li {
float: left;
}
#top-menu a {
display: block;
padding: 5px 25px 7px 25px;
width: 4em;
text-align: center;
-webkit-transition: .5s all ease-out;
-moz-transition: .5s all ease-out;
transition: .5s all ease-out;
border-top: 3px solid white;
color: #aaa;
text-decoration: none;
}
#top-menu a:hover {
color: #000;
}
#top-menu li.active > a {
border-top: 3px solid #333;
color: #333;
}
#zod{
height:400px;
}
#foo{
height:400px;
}
#foo2.1{
height:400px;
display:block
}
#foo2.2{
height:400px;
display:block
}
#bar {
height:400px;
}
#baz {
height:400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="top-menu">
<li>
<a class="scroll-to" href="#zod">zod</a>
</li>
<li>
<a class="scroll-to" href="#foo">Foo</a>
<ul class="articleSubList">
<li><a class="scroll-to" href="#foo2.1">foo 1.1</a></li>
<li><a class="scroll-to" href="#foo2.2">foo 2.2</a></li>
</ul>
</li>
<li>
<a class="scroll-to" href="#bar">Bar</a>
</li>
<li>
<a class="scroll-to" href="#baz">Baz</a>
</li>
</ul>
<div id="zod">Zod</div>
<div id="foo">Foo111</div>
<div id="foo2.1">Foo 1.1</div>
<div id="foo2.2">Foo 1.2</div>
<div id="bar">Bar</div>
<div id="baz">Baz</div>