I am trying to make a drop down menu with Javascript (no JQuery). In my HTML, I have an unordered list that contains two list items, each containing an anchor and an unordered list with more list items inside.
I am not concerned about the menus looking pretty, just that the Javascript works. Right now, I am trying to get it to set up event listeners through a
while loop so that when the top level list items are moused over, the class of the lower list itemsnwill change from one in which the CSS is set to display:none; to a class that has display:block; to make them appear.
I only have two tabs here, but that is because I shortened it from a list of ten tabs. I also don't care about any further submenus.
JS Fiddle: https://jsfiddle.net/xL5pukd5/
HTML:
<header>
<!-- <ul> <li><a></a><ul> <li><a></a></li> -->
<!-- dd-menu dd-menu-tab dd-menu-tab-anchor dd-submenu dd-submenu-tab dd-submenu-tab-anchor -->
<nav id="dd-menu-container">
<ul id="dd-menu">
<li class="dd-menu-tab">
Link 1
<ul class="dd-submenu">
<li class="dd-submenu-tab">
SubLink1a</li>
<li class="dd-submenu-tab">
Sublink1b</li>
</ul>
</li>
<li class="dd-menu-tab">
Link2
<ul class="dd-submenu">
<li class="dd-submenu-tab">
Sublimn2a</li>
<li class="dd-submenu-tab">
Sublink2b</li>
<li class="dd-submenu-tab">
Sublink2c</li>
</ul>
</li>
</nav>
</header>
JavaScript:
getTabs = function(elementClass) {
var tabs = document.getElementsByClassName(elementClass);
return tabs
};
getSubmenus = function(elementClass) {
var submenus = document.getElemenetsByClassName(elementClass);
return submenus;
};
window.onload = function {
var tabs = getTabs("dd-menu-tab");
var submenus = getSubmenus("dd-submenu");
var tabNumber = tabs.length - 1;
var currentTab = 0;
while (currentTab <= tabNumber) {
tabs[currentTab].onmouseover = function {
submenus[currentTab].className = "dd-submenu-onmouseover"
};
tabs[currentTab].onmouseout = function {
submenus[curenetTab].className = "dd-submenu"
};
currentTab += 1;
}
};
CSS:
.dd-submenu {
display: none;
}
.dd-submenu-onmouseover {
display: block;
}
I've tried using other prewritten drop down menu methods, but I couldn't get them to work, so I tried to create my own. Thanks in advance to any help :)
There are multiple issues with your script, including typos are said in the comment and wrong use of a closure variable in a loop.
But you really don't need js to fix this, you can use the :hover css selector to do it like
html,
body {
margin: 0;
padding: 0;
}
nav {
display: block;
padding: 0px;
margin: 0;
top: 0;
left: 0;
width: 100%;
background-color: beige;
}
.dd-menu-tab-anchor {
display: inline-block;
color: white;
}
.dd-menu {
position: absolute;
width: 100%;
top: 0;
left: 0;
}
.dd-menu-tab {
float: left;
padding-top: 10px;
padding-bottom: 10px;
padding-left: auto;
padding-right: auto;
width: 9%;
text-align: center;
}
.dd-submenu {
display: none;
}
.dd-menu-tab:hover .dd-submenu {
display: block;
}
li {
list-style-type: none;
background-color: #3300FF;
border: 2px;
border-color: white;
}
a {
text-align: center;
}
<header>
<!-- <ul> <li><a></a><ul> <li><a></a></li> -->
<!-- dd-menu dd-menu-tab dd-menu-tab-anchor dd-submenu dd-submenu-tab dd-submenu-tab-anchor -->
<nav id="dd-menu-container">
<ul id="dd-menu">
<li id="dd-menu-tab1" class="dd-menu-tab">
Link 1
<ul id="dd-submenu1" class="dd-submenu">
<li id="dd-submenu1-tab1" class="dd-submenu-tab">
SubLink1a
</li>
<li id="dd-submenu1-tab2" class="dd-submenu-tab">
Sublink1b
</li>
</ul>
</li>
<li id="dd-menu-tab2" class="dd-menu-tab">
Link2
<ul id="dd-submenu2" class="dd-submenu">
<li id="dd-submenu2-tab1" class="dd-submenu-tab">
Sublimn2a
</li>
<li id="dd-submenu2-tab2" class="dd-submenu-tab">
Sublink2b
</li>
<li id="dd-submenu2-tab3" class="dd-submenu-tab">
Sublink2c
</li>
</ul>
</li>
</nav>
</header>
<div>
<h1 id="site title">Website.com</h1>
</div>
<body>
<p>First paragraph sample text</p>
<p>Second paragraph sample text</p>
<p>Third paragraph sample text</p>
</body>
<footer>
<p>
References
</p>
<!-- <script src="file.js" type="text/javascript"></script> -->
</footer>
</html>
If you still want to continue using your logic
getTabs = function(elementClass) {
var tabs = document.getElementsByClassName(elementClass);
console.log(tabs);
return tabs
};
getSubmenus = function(elementClass) {
var submenus = document.getElementsByClassName(elementClass);
console.log(submenus);
return submenus;
};
window.onload = function() {
var tabs = getTabs("dd-menu-tab");
var submenus = getSubmenus("dd-submenu");
var tabNumber = tabs.length - 1;
var currentTab = 0;
[].forEach.call(tabs, function(el, idx) {
var submenu = submenus[idx];
el.addEventListener('mouseenter', function() {
submenu.className = "dd-submenu-onmouseover"
});
el.addEventListener('mouseleave', function() {
submenu.className = "dd-submenu"
});
})
};
html,
body {
margin: 0;
padding: 0;
}
nav {
display: block;
padding: 0px;
margin: 0;
top: 0;
left: 0;
width: 100%;
background-color: beige;
}
.dd-menu-tab-anchor {
display: inline-block;
color: white;
}
.dd-menu {
position: absolute;
width: 100%;
top: 0;
left: 0;
}
.dd-menu-tab {
float: left;
padding-top: 10px;
padding-bottom: 10px;
padding-left: auto;
padding-right: auto;
width: 9%;
text-align: center;
}
.dd-submenu {
display: none;
}
.dd-submenu-onmouseover {
display: block;
}
li {
list-style-type: none;
background-color: #3300FF;
border: 2px;
border-color: white;
}
a {
text-align: center;
}
<header>
<!-- <ul> <li><a></a><ul> <li><a></a></li> -->
<!-- dd-menu dd-menu-tab dd-menu-tab-anchor dd-submenu dd-submenu-tab dd-submenu-tab-anchor -->
<nav id="dd-menu-container">
<ul id="dd-menu">
<li id="dd-menu-tab1" class="dd-menu-tab">
Link 1
<ul id="dd-submenu1" class="dd-submenu">
<li id="dd-submenu1-tab1" class="dd-submenu-tab">
SubLink1a
</li>
<li id="dd-submenu1-tab2" class="dd-submenu-tab">
Sublink1b
</li>
</ul>
</li>
<li id="dd-menu-tab2" class="dd-menu-tab">
Link2
<ul id="dd-submenu2" class="dd-submenu">
<li id="dd-submenu2-tab1" class="dd-submenu-tab">
Sublimn2a
</li>
<li id="dd-submenu2-tab2" class="dd-submenu-tab">
Sublink2b
</li>
<li id="dd-submenu2-tab3" class="dd-submenu-tab">
Sublink2c
</li>
</ul>
</li>
</nav>
</header>
<div>
<h1 id="site title">Website.com</h1>
</div>
<body>
<p>First paragraph sample text</p>
<p>Second paragraph sample text</p>
<p>Third paragraph sample text</p>
</body>
<footer>
<p>
References
</p>
</footer>
Demo: Fiddle
getTabs = function(elementClass) {
var tabs = document.getElementsByClassName(elementClass);
console.log(tabs);
return tabs
};
getSubmenus = function(elementClass) {
var submenus = document.getElemenetsByClassName(elementClass);
console.log(submenus);
return submenus;
};
window.onload = function {
var tabs = getTabs("dd-menu-tab");
var submenus = getSubmenus("dd-submenu");
var tabNumber = tabs.length - 1;
var currentTab = 0;
while (currentTab <= tabNumber) {
tabs[currentTab].onmouseover = function {
submenus[currentTab].className = "dd-submenu-onmouseover"
};
tabs[currentTab].onmouseout = function {
submenus[curenetTab].className = "dd-submenu"
};
currentTab += 1;
}
};
html,
body {
margin: 0;
padding: 0;
}
nav {
display: block;
padding: 0px;
margin: 0;
top: 0;
left: 0;
width: 100%;
background-color: beige;
}
.dd-menu-tab-anchor {
display: inline-block;
color: white;
}
.dd-menu {
position: absolute;
width: 100%;
top: 0;
left: 0;
}
.dd-menu-tab {
float: left;
padding-top: 10px;
padding-bottom: 10px;
padding-left: auto;
padding-right: auto;
width: 9%;
text-align: center;
}
.dd-submenu {
display: none;
}
.dd-menu-tab:hover .dd-submenu {
position:absolute;
display: block;
}
li {
list-style-type: none;
background-color: #3300FF;
border: 2px;
border-color: white;
}
a {
text-align: center;
}
<header>
<!-- <ul> <li><a></a><ul> <li><a></a></li> -->
<!-- dd-menu dd-menu-tab dd-menu-tab-anchor dd-submenu dd-submenu-tab dd-submenu-tab-anchor -->
<nav id="dd-menu-container">
<ul id="dd-menu">
<li id="dd-menu-tab1" class="dd-menu-tab">
Link 1
<ul id="dd-submenu1" class="dd-submenu">
<li id="dd-submenu1-tab1" class="dd-submenu-tab">
SubLink1a</li>
<li id="dd-submenu1-tab2" class="dd-submenu-tab">
Sublink1b</li>
</ul>
</li>
<li id="dd-menu-tab2" class="dd-menu-tab">
Link2
<ul id="dd-submenu2" class="dd-submenu">
<li id="dd-submenu2-tab1" class="dd-submenu-tab">
Sublimn2a</li>
<li id="dd-submenu2-tab2" class="dd-submenu-tab">
Sublink2b</li>
<li id="dd-submenu2-tab3" class="dd-submenu-tab">
Sublink2c</li>
</ul>
</li>
</nav>
</header>
<div>
<h1 id="site title">Website.com</h1>
</div>
<body>
<p>First paragraph sample text</p>
<p>Second paragraph sample text</p>
<p>Third paragraph sample text</p>
</body>
<footer>
<p>
References
</p>
<!-- <script src="file.js" type="text/javascript"></script> -->
</footer>
</html>
Related
This simple code will work:
<!DOCTYPE html>
<html>
<body>
<br>
<div>
Link 1
Link 2
Link 3
Link 4
</div>
Link 1 <span>color me plz!</span>
<br><br>
<div>
Link 1
Link 2
Link 3
Link 4
</div>
Link 1 <span>color me plz!</span>
Link 2 <span>color me plz!</span>
<script>
testing = document.querySelectorAll("div + a");
for (let i = 0; i < testing.length; i++){
testing[i].addEventListener("mouseenter", function(){
const x = testing[i];
x.style.backgroundColor = "cyan";
x.querySelector("span").style.backgroundColor = "pink";
});
}
</script>
</body>
</html>
But my real problem is this:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style-test.css">
</head>
<body>
<nav>
<div>
Menu A.1
Menu A.2
</div>
<div class="right">
<div>
Menu B.1
<ul>
<li>B.1 Sub 1</li>
<li>B.1 Sub 2</li>
</ul>
</div>
<div>
Menu B.2
<ul>
<li>B.2 Sub 1</li>
<li>B.2 Sub 2</li>
</ul>
</div>
<div>
Menu B.3
<ul>
<li>B.3 Sub 1 Test Longer Text</li>
<li>B.3 Sub 2</li>
</ul>
</div>
<div>
Menu B.4
<ul>
<li>B.4 Sub 1</li>
<li>B.4 Sub 2</li>
</ul>
</div>
</div>
</nav>
<div style="float: left; margin-top: 1000px"></div>
<script>
const menu = document.querySelectorAll("nav div > a");
console.log("Get all menu:");
console.log(menu);
for (let i = 0; i < menu.length; i++) {
menu[i].addEventListener("mouseenter", function(){
console.log("mouseEnter");
console.log("menu[i].addEventListener:");
console.log(menu[i]);
//console.log(menu[i].querySelector("+ ul"));
const subMenu = menu[i].querySelector(` + ul`);
/* ^^^ The problem is here above ^^^ --- Everything stops here */
console.log("OK! 'querySelector' is valid"); //<-- this won't display...
//if(window.getComputedStyle(subMenu).getPropertyValue("display") === "block") /*Corrected*/
if(subMenu.style.display === "block")
{
console.log("subMenu.style.display === block");
const subMenuBox = subMenu.getBoundingClientRect();
const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
if(posX > 0)
subMenu.style.left = (-posX - 20) + "px";
/*padding problem (need -20): didn't .getBoundingClientRect() include padding?*/
// /*or just*/ subMenu.style.right = "0px";
console.log("When here successfully!");
}
else{
console.log("Failed...");
}
})
}
</script>
</body>
</html>
css:
body{
color: #fff;
font-size: 20px;
background: #999;
margin: 0;
}
a{
color: #cfc;
}
nav, nav div > a, nav div{
float: left;
}
nav{
width: 100%;
background: #4169E1;
white-space: nowrap;
}
nav div{
margin: 0em 0.2em;
}
nav div:first-child{
margin-left: 0;
}
nav div div{
position: relative;
margin: 0;
}
nav a{
color: #cff;
display: block;
font-weight: 600;
text-decoration: none;
padding: 0.5em 1em;
}
nav a:hover{
color: #fff;
background: #f90;
}
nav div > a + ul{
position: absolute;
top: 2.15em;
float: left;
background: #666;
list-style-type: none;
margin: 0;
padding: 0;
min-width: 180px;
display: none;
}
nav div:hover > a + ul{
display: block;
}
.right{
float: right;
}
I just want to get the sibling of <a> just next to it, the <ul> after <a>, which is a + ul in css
<div>
Menu B.1
<ul>
<li>B.1 Sub 1</li>
<li>B.1 Sub 2</li>
</ul>
</div>
Focus here:
<script>
const menu = document.querySelectorAll("nav div > a");
for (let i = 0; i < menu.length; i++) {
menu[i].addEventListener("mouseenter", function(){
//Problem is here...
const subMenu = menu[i].querySelector(" + ul");
/*Corrected*/
//if(window.getComputedStyle(subMenu).getPropertyValue("display") === "block")
if(subMenu != null && subMenu.style.display === "block")
{
const subMenuBox = subMenu.getBoundingClientRect();
const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
if(posX > 0)
subMenu.style.left = (-posX -20) + "px";
/*padding problem (need -20): didn't .getBoundingClientRect() include
padding?*/
// /*or just*/ subMenu.style.right = "0px";
}
})
}
</script>
Now, what I done here:
First, I select all the <a> inside <nav>.
Then using for() loop and put mouseenter event to all the selected <a>.
When user hover over the <a>, the mouseenter know exactly which <a> was hover.
Now here's the problem: I want to select the a + ul of hovered <a>.
I have tried this:
console.log(document.querySelector("menu[i] + ul"));
gives me a null value
console.log(document.querySelector(menu[i] + " + ul"));
gives me SyntaxError: 'file:///C:/Users/path/path/thisPage.html + ul' is not a valid selector
console.log(menu[i].querySelector(" + ul"));
gives me SyntaxError: '+ul' is not a valid selector
How can I fix this? What is the right thing to do with .querySelector() for continuing selection but with adjacent sibling tag?
On one hand, on non-ancient browsers, you can use :scope to indicate the element on which the querySelector is being called on. But querySelector will only select elements which are children of the current element, and the <ul> you want is a sibling.
Take the nextElementSibling of the element instead, check that it exists, and check that it's a ul instead:
for (const menu of document.querySelectorAll("nav div > a")) {
menu.addEventListener("mouseenter", function() {
console.log(menu, menu.nextElementSibling);
const subMenu = menu.nextElementSibling;
if (!subMenu || !subMenu.matches('ul')) {
return;
}
if (subMenu.style.display === "block") {
console.log("subMenu.style.display === block");
const subMenuBox = subMenu.getBoundingClientRect();
const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
subMenu.style.left = -posX;
console.log("When here successfully!");
} else {
console.log("Failed...");
}
})
}
body {
color: #fff;
font-size: 20px;
background: #999;
margin: 0;
}
a {
color: #cfc;
}
nav,
nav div>a,
nav div {
float: left;
}
nav {
width: 100%;
background: #4169E1;
white-space: nowrap;
}
nav div {
margin: 0em 0.2em;
}
nav div:first-child {
margin-left: 0;
}
nav div div {
position: relative;
margin: 0;
}
nav a {
color: #cff;
display: block;
font-weight: 600;
text-decoration: none;
padding: 0.5em 1em;
}
nav a:hover {
color: #fff;
background: #f90;
}
nav div>a+ul {
position: absolute;
top: 2.15em;
float: left;
background: #666;
list-style-type: none;
margin: 0;
padding: 0;
min-width: 180px;
display: none;
}
nav div:hover>a+ul {
display: block;
}
.right {
float: right;
}
<nav>
<div>
Menu A.1
Menu A.2
</div>
<div class="right">
<div>
Menu B.1
<ul>
<li>B.1 Sub 1</li>
<li>B.1 Sub 2</li>
</ul>
</div>
<div>
Menu B.2
<ul>
<li>B.2 Sub 1</li>
<li>B.2 Sub 2</li>
</ul>
</div>
<div>
Menu B.3
<ul>
<li>B.3 Sub 1 Test Longer Text</li>
<li>B.3 Sub 2</li>
</ul>
</div>
<div>
Menu B.4
<ul>
<li>B.4 Sub 1</li>
<li>B.4 Sub 2</li>
</ul>
</div>
</div>
</nav>
<div style="float: left; margin-top: 1000px "></div>
Because not all menus have sub-menus, you do need to test if the UL exists first before trying to do stuff with it.
Note that subMenu.style.display === "block" is never fulfilled in the given code because the <ul>s don't have style properties directly on the elements. If they did, the test would succeed. If you're trying to see if they currently are being displayed, use window.getComputedStyle instead:
for (const menu of document.querySelectorAll("nav div > a")) {
menu.addEventListener("mouseenter", function() {
console.log(menu, menu.nextElementSibling);
const subMenu = menu.nextElementSibling;
if (!subMenu || !subMenu.matches('ul')) {
return;
}
if (window.getComputedStyle(subMenu).display === "block") {
console.log("subMenu.style.display === block");
const subMenuBox = subMenu.getBoundingClientRect();
const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
subMenu.style.left = -posX;
console.log("When here successfully!");
} else {
console.log("Failed...");
}
})
}
body {
color: #fff;
font-size: 20px;
background: #999;
margin: 0;
}
a {
color: #cfc;
}
nav,
nav div>a,
nav div {
float: left;
}
nav {
width: 100%;
background: #4169E1;
white-space: nowrap;
}
nav div {
margin: 0em 0.2em;
}
nav div:first-child {
margin-left: 0;
}
nav div div {
position: relative;
margin: 0;
}
nav a {
color: #cff;
display: block;
font-weight: 600;
text-decoration: none;
padding: 0.5em 1em;
}
nav a:hover {
color: #fff;
background: #f90;
}
nav div>a+ul {
position: absolute;
top: 2.15em;
float: left;
background: #666;
list-style-type: none;
margin: 0;
padding: 0;
min-width: 180px;
display: none;
}
nav div:hover>a+ul {
display: block;
}
.right {
float: right;
}
<nav>
<div>
Menu A.1
Menu A.2
</div>
<div class="right">
<div>
Menu B.1
<ul>
<li>B.1 Sub 1</li>
<li>B.1 Sub 2</li>
</ul>
</div>
<div>
Menu B.2
<ul>
<li>B.2 Sub 1</li>
<li>B.2 Sub 2</li>
</ul>
</div>
<div>
Menu B.3
<ul>
<li>B.3 Sub 1 Test Longer Text</li>
<li>B.3 Sub 2</li>
</ul>
</div>
<div>
Menu B.4
<ul>
<li>B.4 Sub 1</li>
<li>B.4 Sub 2</li>
</ul>
</div>
</div>
</nav>
<div style="float: left; margin-top: 1000px "></div>
I want to create a sidebar with a submenu that extends to the right instead of down. A rough picture below:
I'm fairly new with flexbox and that is all I could come up with:
HTML:
<div class="d-flex align-items-stretch">
<nav class="side-navigation">
<ul class="sidebar-nav d-flex flex-column">
<li class="sidebar-nav-expand d-flex toggle">
<a class="nav-link sidebar-nav-expand-toggle py-3" href="#">
<span class="icon icon-ic_compute"></span>
<span class="nav-item-text">Menu1</span>
<span class="float-right"><i class="fas fa-mortar-pestle"></i></span>
</a>
<div class="sidebar-nav-expand-items">
<ul class="sidebar-nav">
<li class="sidebar-nav-item py-3">
<a class="nav-link py-1" href="#some-url1">
<span class="nav-item-text">Menu1 subitem1</span>
</a>
</li>
<li class="sidebar-nav-item py-3">
<a class="nav-link py-1" href="#some-url2">
<span class="nav-item-text">Menu1 subitem2</span>
</a>
</li>
<li class="sidebar-nav-item py-3">
<a class="nav-link py-1" href="#some-url3">
<span class="nav-item-text">Menu1 subitem3</span>
</a>
</li>
</ul>
</div>
</li>
<li class="sidebar-nav-expand d-flex">
<a class="nav-link sidebar-nav-expand-toggle py-3" href="#">
<span class="icon icon-ic_management"></span>
<span class="nav-item-text">Menu2</span>
<span class="float-right"><i class="fas fa-mortar-pestle"></i></span>
</a>
<div class="sidebar-nav-expand-items">
<ul class="sidebar-nav">
<li class="sidebar-nav-item py-3">
<a class="nav-link py-1" href="#some-url21">
<span class="nav-item-text">Menu2 subitem1</span>
</a>
</li>
<li class="sidebar-nav-item py-3">
<a class="nav-link py-1" href="#some-url22">
<span class="nav-item-text">Menu2 subitem2</span>
</a>
</li>
<li class="sidebar-nav-item py-3">
<a class="nav-link py-1" href="#some-url23">
<span class="nav-item-text">Menu2 subitem3</span>
</a>
</li>
</ul>
</div>
</li>
<li class="sidebar-nav-expand">
<a class="nav-link py-3" href="#some-url">
<span class="icon icon-ic_administration"></span>
<span class="nav-item-text">Direct link</span>
<span class="float-right"><i class="fas fa-mortar-pestle"></i></span>
</a>
</li>
</ul>
</nav>
<div class="main">
Content
</div>
</div>
SCSS:
$white: #fff;
$whitesmoke: #ECEFF1;
$aliceblue: #f1f6ff;
$light-gray: #D6DADC;
$blue: #2971FB;
$light-blue: #BFD5FE;
$black: #263238;
$light-black: #364046;
$orange: #FF8F00;
.side-navigation {
max-width: 540px;
transition: all 0.3s;
}
.sidebar-nav {
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
padding-left: 0;
margin-bottom: 0;
list-style: none;
.nav-link {
display: block;
padding: .75rem 1rem;
color: $white;
text-decoration: none;
background: 0 0;
&:hover {
color: $orange;
}
}
.sidebar-nav-item {
width: 100%;
}
.sidebar-nav-expand {
width: 100%;
background-color: $black;
&.toggle {
.sidebar-nav-expand-toggle {
color: $orange;
}
.sidebar-nav-expand-items {
margin-left: 0;
}
}
.sidebar-nav-expand-toggle {
width: 270px;
z-index: 2;
}
.sidebar-nav-expand-items {
width: 270px;
height: 100%;
background-color: $light-gray;
transition: margin-left .25s ease-in-out;
margin-left: -270px;
color: $black;
.nav-link {
color: $black;
&:hover {
color: $orange;
}
&:not(.active) {
margin-left: 8px;
}
&.active {
border-left: solid 8px $orange;
}
}
}
}
}
.main {
width: 100%;
min-height: 100vh;
padding-left: 10px;
}
JS:
$('.sidebar-nav-expand-toggle').on('click', function() {
let self = $(this);
self.parent('.sidebar-nav-expand').toggleClass('toggle');
$('.main').toggleClass('toggle');
});
https://jsfiddle.net/cj4375er/
As you can see the child is not properly hidden under the parent and submenus are stacked, if a menu is opened I want all other menus to close. Also, there is too much space between menu items.
I would just need some points in the right direction. If there is an example of a similar case I would be happy.
I see that you tried to cover the submenu bar using z-index: 2 at that sidebar-nav-expand-toggle. However, your items are not properly hidden because your sidebar-nav-expand-toggle has no background (i.e. it's transparent). The background is actually from sidebar-nav-expand (#263238) that bleeds through the sidebar-nav-expand-toggle because sidebar-nav-expand-toggle is transparent. Therefore, the solution is to instruct the sidebar-nav-expand-toggle to inherit the same background as its parent, resulting in this code below:
.sidebar-nav-expand-toggle {
width: 270px;
z-index: 2;
background: inherit;
}
Your menus are stacked because you did not handle the closing of the other submenus that may be open (you merely toggled the one that is clicked, but you didn't close the ones that may be open that are not clicked). A simple addition to your JS code fixes this:
$('.sidebar-nav-expand-toggle').on('click', function() {
let self = $(this);
let toggled = self.parent('.sidebar-nav-expand').hasClass('toggle')
hideAllSubmenu()
if (toggled)
self.parent('.sidebar-nav-expand').removeClass('toggle')
else
self.parent('.sidebar-nav-expand').addClass('toggle')
$('.main').toggleClass('toggle');
});
function hideAllSubmenu() {
let submenus = $('.sidebar-nav-expand-toggle')
submenus.parent('.sidebar-nav-expand').removeClass('toggle')
}
There is too much space between menu items. My suggestion would be to make a flexbox that has only the three menu items with padding. And then, for each of those menus (can be a or div), create a div inside it that has position: absolute based on those menus. You can then slide it out when clicked by using transform: translate. That way, the height is not dependent on a wrapping container's height.
Also, I see that you are using SCSS. I really suggest reading about BEM selectors here to better utilize SCSS to create a more easily readable code.
EDIT:
I've added a little working example of what I meant by using position: absolute and still being able to push the main content to the right when the submenu expands. Take a look below (please adjust this minimal working example to what you need):
function resetSubmenuStyles() {
let submenus = document.querySelectorAll('.submenu div')
for (let submenu of submenus) {
submenu.style.width = ''
submenu.style.visibility = ''
}
}
document.querySelector('#menuOne').addEventListener('click', e => {
let submenu = document.querySelector('.submenu')
let submenuOne = document.querySelector('#submenuOne')
let submenuStyle = window.getComputedStyle(submenuOne)
let submenuWidth = parseInt(submenuStyle.getPropertyValue('width')) - parseInt(submenuStyle.getPropertyValue('padding-left')) - parseInt(submenuStyle.getPropertyValue('padding-right'))
resetSubmenuStyles()
if (submenuWidth == 0) {
submenu.style.width = '20%'
submenuOne.style.width = '100%'
submenuOne.style.visibility = 'visible'
}
else {
submenu.style.width = ''
submenuOne.style.width = ''
submenuOne.style.visibility = ''
}
})
document.querySelector('#menuTwo').addEventListener('click', e => {
let submenu = document.querySelector('.submenu')
let submenuTwo = document.querySelector('#submenuTwo')
let submenuStyle = window.getComputedStyle(submenuTwo)
let submenuWidth = parseInt(submenuStyle.getPropertyValue('width')) - parseInt(submenuStyle.getPropertyValue('padding-left')) - parseInt(submenuStyle.getPropertyValue('padding-right'))
resetSubmenuStyles()
if (submenuWidth == 0) {
submenu.style.width = '20%'
submenuTwo.style.width = '100%'
submenuTwo.style.visibility = 'visible'
}
else {
submenu.style.width = ''
submenuTwo.style.width = ''
submenuTwo.style.visibility = ''
}
})
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: Helvetica, sans-serif;
}
html, body {
width: 100vw;
height: 100%;
}
body {
display: flex;
}
.menu {
width: 20%;
height: 100%;
background: #000;
color: #FFF;
}
.menu h1 {
padding: 20px;
cursor: pointer;
}
.submenu {
position: relative;
width: 0%;
height: 100%;
background: #333;
color: #FFF;
}
.submenu div {
visibility: hidden;
position: absolute;
width: 0;
top: 0;
left: 0;
padding: 20px;
}
<div class = "menu">
<h1 id = "menuOne">Test</h1>
<h1 id = "menuTwo">Test</h1>
</div>
<div class = "submenu">
<div id = "submenuOne">
Submenu Test One
</div>
<div id = "submenuTwo">
Submenu Test Two
</div>
</div>
<div id = "main">Some Content</div>
I have a navigation of 8 items (li), and when resolution gets smaller items drop in a new line. I need to make it so that when an item doesn't fit on a navigation anymore it should put a "MORE" dropdown button on the right side of nav and put the item that doesn't fit in a dropdown.
I don't know how I can do it with the Angular project, I don't need JQuery code.
This is my code:
<div id="mainMenu" class="main-menu">
<ul id="autoNav" class="main-nav">
<li>
home
</li>
<li>
about us
</li>
<li>
portfolio
</li>
<li>
team
</li>
<li>
blog
</li>
<li>
contact
</li>
<li id="autoNavMore" class="auto-nav-more">
more
<ul id="autoNavMoreList" class="auto-nav-more-list">
<li>
policy
</li>
</ul>
</li>
</ul>
</div>
Javascript Code:
const $mainMenu = $("#mainMenu");
const $autoNav = $("#autoNav");
const $autoNavMore = $("#autoNavMore");
const $autoNavMoreList = $("#autoNavMoreList");
autoNavMore = () => {
let childNumber = 2;
if($(window).width() >= 320) {
// GET MENU AND NAV WIDTH
const $menuWidth = $mainMenu.width();
const $autoNavWidth = $autoNav.width();
if($autoNavWidth > $menuWidth) {
// CODE FIRES WHEN WINDOW SIZE GOES DOWN
$autoNav.children(`li:nth-last-child(${childNumber})`).prependTo($autoNavMoreList);
autoNavMore();
} else {
// CODE FIRES WHEN WINDOW SIZE GOES UP
const $autoNavMoreFirst = $autoNavMoreList.children('li:first-child').width();
// CHECK IF ITEM HAS ENOUGH SPACE TO PLACE IN MENU
if(($autoNavWidth + $autoNavMoreFirst) < $menuWidth) {
$autoNavMoreList.children('li:first-child').insertBefore($autoNavMore);
}
}
if($autoNavMoreList.children().length > 0) {
$autoNavMore.show();
childNumber = 2;
} else {
$autoNavMore.hide();
childNumber = 1;
}
}
}
// INIT
autoNavMore();
$(window).resize(autoNavMore);
CSS Code:
.main-menu {
max-width: 800px;
}
.main-nav {
display: inline-flex;
padding: 0;
list-style: none;
}
.main-nav li a {
padding: 10px;
text-transform: capitalize;
white-space: nowrap;
font-size: 30px;
font-family: sans-serif;
text-decoration: none;
}
.more-btn {
color: red;
}
.auto-nav-more {
position: relative;
}
.auto-nav-more-list {
position: absolute;
right: 0;
opacity: 0;
visibility: hidden;
transition: 0.2s;
text-align: right;
padding: 0;
list-style: none;
background: grey;
border-radius: 4px;
}
.auto-nav-more:hover .auto-nav-more-list {
opacity: 1;
visibility: visible;
}
Any help to how I can do it with the Angular project (Typescript)?.
You can use the ng-repeat-start (ngRepeat directive)
https://docs.angularjs.org/api/ng/directive/ngRepeat
Example
<header ng-repeat-start="item in items">
Header {{ item }}
</header>
<div class="body">
Body {{ item }}
</div>
<footer ng-repeat-end>
Footer {{ item }}
</footer>
As the title states, I am wanting my hamburger navbar to close when I click on the tags I have tried many ways for the last couple hours but am unable to solve my problem?
I Have tried setting the hide() property with jquery but no luck think it may be because i am pretty new to JS and am just wanting to get my website finished.
const menuBtn = document.querySelector(".menu-btn");
const mobileContent = document.querySelector(".mobile-content");
const mobileItem = document.querySelector(".mobile-item");
const mobileItems = document.querySelectorAll(".mobile-items");
// Set Initial State Of Menu
let showMenu = false;
menuBtn.addEventListener("click", toggleMenu);
function toggleMenu() {
if (!showMenu) {
menuBtn.classList.add("close");
mobileContent.classList.add("show");
mobileItem.classList.add("show");
mobileItems.forEach(item => item.classList.add("show"));
// Set Menu State
showMenu = true;
} else {
menuBtn.classList.remove("close");
mobileContent.classList.remove("show");
mobileItem.classList.remove("show");
mobileItems.forEach(item => item.classList.remove("show"));
// Set Menu State
showMenu = false;
}
}
.mobile-nav {
display: block;
position: fixed;
width: 100%;
top: 0;
z-index: 3;
}
.mobile-nav .menu-btn {
position: absolute;
z-index: 3;
right: 20px;
top: 20px;
cursor: pointer;
}
.mobile-nav .menu-btn .btn-line {
width: 28px;
height: 3px;
margin: 0 0 5px 0;
background: #333;
}
.mobile-content {
position: fixed;
top: 0;
width: 100%;
opacity: 0.9;
visibility: hidden;
}
.mobile-content.show {
visibility: visible;
}
.mobile-content .mobile-item {
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
float: right;
width: 100%;
height: 100vh;
overflow: hidden;
margin: 0;
padding: 0;
background: blue;
list-style: none;
transform: translate3d(0, -100%, 0);
}
.mobile-content .mobile-link {
display: inline-block;
position: relative;
font-size: 2rem;
padding: 1rem 0;
font-weight: bold;
color: #333;
text-decoration: none;
}
<!-- Mobile Nav -->
<div class="mobile-nav">
<div class="menu-btn">
<div class="btn-line"></div>
<div class="btn-line"></div>
<div class="btn-line"></div>
</div>
<h2>MATTY</h2>
<nav class="mobile-content">
<ul class="mobile-item">
<li class="mobile-items">
<a href="#about-me" class="mobile-link">
ABOUT
</a>
</li>
<li class="mobile-items">
<a href="#the-portfolio" class="mobile-link">
PORTFOLIO
</a>
</li>
<li class="mobile-items">
<a href="#" class="mobile-link">
BLOG
</a>
</li>
<li class="mobile-items">
<a href="#contact-me" class="mobile-link">
CONTACT
</a>
</li>
</ul>
</nav>
</div>
I had to remove some of your CSS as it was not working in the snippet.
Recommend you use element.classList.toggle() as below.
Note how much simpler the code becomes.
EDIT: Clicking any a tag will now close menu
document.addEventListener("click", (e) => {
if(e.target.matches('.menu-btn')
|| e.target.matches('.btn-line')
|| e.target.matches('a')) {
toggleMenu();
}
});
function toggleMenu() {
document.querySelector('.mobile-content').classList.toggle('hide');
}
.btn-line {
display: block;
width: 50px;
margin: 5px;
border: 2px solid black;
}
.mobile-nav {
display: block;
width: 100%;
z-index: 3;
}
.mobile-content {
position: fixed;
width: 100%;
opacity: 0.9;
}
.hide {
display: none;
}
<!-- Mobile Nav -->
<div class="mobile-nav">
<div class="menu-btn">
<span class="btn-line"></span>
<span class="btn-line"></span>
<span class="btn-line"></span>
</div>
<a href="#home">
<h2>MATTY</h2>
</a>
<nav class="mobile-content hide">
<ul class="mobile-item">
<li class="mobile-items">
<a href="#about-me" class="mobile-link">
ABOUT
</a>
</li>
<li class="mobile-items">
<a href="#the-portfolio" class="mobile-link">
PORTFOLIO
</a>
</li>
<li class="mobile-items">
<a href="#" class="mobile-link">
BLOG
</a>
</li>
<li class="mobile-items">
<a href="#contact-me" class="mobile-link">
CONTACT
</a>
</li>
</ul>
</nav>
</div>
#MPB A good way to dabble into some simple JQuery language is a way to fix your problem. A quick and easy way to make a good Hamburger Navigation menue is with the toggleClass(); function in JQuery. Just make a #keyframes-animation within an un-set class and toggleClass(); will switch between the two seamlessly. I do this all the time, comment if you'd like me to forward the code to you for you to use.
I have a simple tabbed gallery which I managed to make with setAttribute.
But sometimes when I click in one of my thumbnails, setAttribute returns "null"
not always but sometimes it does.
And I'm not understanding why is this happening.
I'll appreciate your help.
here is a link to code:
code
and here is my code:
var tabs = document.querySelector('.tabs');
var tab = document.querySelectorAll('.tab');
var showTab = document.querySelector('.showtab');
var img = document.querySelector('.showtabimg');
tab.forEach(thumbNail => {
thumbNail.addEventListener('click', function(item) {
// delete all active or normal elements active class
tab.forEach(i => i.classList.remove('active'));
var content = item.target.getAttribute("src");
this.classList.toggle('active')
img.setAttribute('src', content);
});
});
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul li{
list-style: none;
}
.showtab{
width: 200px;
height: 100px;
color: red;
font-weight: bold;
display: flex;
margin-bottom: 20px;
}
.showtab img{
width: 100%;
}
.tabs{
display: flex;
}
.tabs li{
display: flex;
cursor: pointer;
width: 100px;
height: 100px;
margin-right: 10px;
}
.tabs li img{
width: 100%;
}
.active{
opacity: 1 !important;
}
.inActive{
opacity: .3;
}
<div class="tab-container">
<div class="showtab active">
<img class='showtabimg' src="https://images.unsplash.com/photo-1516308354296-1c9c5b561e0b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80" alt="showtabimg">
</div>
<ul class="tabs">
<li class="tab tab1 inActive active">
<img src="https://images.unsplash.com/photo-1516308354296-1c9c5b561e0b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80" alt="foto1" class='img '>
</li>
<li class="tab tab2 inActive">
<img src="https://images.unsplash.com/photo-1513346940221-6f673d962e97?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80" alt="foto2" class='img'>
</li>
<li class="tab tab3 inActive">
<img src="https://images.unsplash.com/photo-1475489991311-e12f9e89705e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1352&q=80" alt="foto3" class='img'>
</li>
</ul>
</div>
PS:
I have read this but it doesn't answer my question.
document.getElementById sometimes returns null