HTML drop-down menu with icons and text - javascript

I have taken over the code from this post. As I have explained there, I have basically no HTML knowledge, yet I have this HTML part of my project that I have to finish.
So, the code creates a bar with a drop-down menu, but I want to extend it to have an icon next to the names. I have looked at these examples but I can't figure out how to combine them. Is there someone who could help?
const projectsTab = document.getElementById('projects')
const tabName = projectsTab.querySelector('.tab-name')
const projectLinks = document.querySelector('.project-links')
projectsTab.addEventListener('click', e => {
const isOpen = projectLinks.classList.contains('open')
if (isOpen) projectLinks.classList.remove('open')
else projectLinks.classList.add('open')
})
// link event listeners
const links = [...projectLinks.children] // turn this into an array
links.forEach(link => link.addEventListener('click', e => {
tabName.innerText = link.innerText
}))
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'Segoe UI', sans-serif;
}
nav {
position: fixed;
width: 100%;
height: 50px;
background: #222;
top: 0;
left: 0;
color: white;
display: flex;
}
nav>* {
flex: 1;
}
#logo {
padding-left: 20px;
font-size: 30px;
text-transform: uppercase;
letter-spacing: 2px;
display: flex;
justify-content: flex-start;
align-items: center;
}
nav ul {
display: flex;
list-style: none;
height: 100%;
}
nav ul li {
position: relative;
flex: 1;
background: #222;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: 0.2s;
}
nav ul li:hover {
background: #555;
}
.project-links {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
left: 0;
top: 100%;
width: 100%;
background-color: white;
color: black;
/* This is the height of this div + height of the nav bar */
transform: translateY(-135%);
transition: 0.2s;
z-index: -1;
}
.project-links.open {
transform: translateY(0);
}
.project-link {
height: 50px;
display: flex;
align-items: center;
padding-left: 20px;
text-decoration: none;
color: white;
cursor: pointer;
transition: 0.2s;
background: #222;
color: white;
}
.project-link:hover {
background: #555;
}
<nav>
<div id="logo">Logo</div>
<ul>
<li>About</li>
<li id="projects">
<span class="tab-name">Projects</span>
<div class="project-links">
<a class="project-link" href="#">Link 1</a>
<a class="project-link" href="#">Link 2</a>
<a class="project-link" href="#">Link 3</a>
</div>
</li>
<li>Contact</li>
</ul>
</nav>

Assuming you want to use Font Awesome icons, just add the proper i tag referencing the right icon beside the text in the corresponding a tags.
const projectsTab = document.getElementById('projects')
const tabName = projectsTab.querySelector('.tab-name')
const projectLinks = document.querySelector('.project-links')
projectsTab.addEventListener('click', e => {
const isOpen = projectLinks.classList.contains('open')
if (isOpen) projectLinks.classList.remove('open')
else projectLinks.classList.add('open')
})
// link event listeners
const links = [...projectLinks.children] // turn this into an array
links.forEach(link => link.addEventListener('click', e => {
tabName.innerText = link.innerText
}))
document.addEventListener("click", e => {
if(!projectsTab.contains(e.target)){
projectLinks.classList.remove("open");
}
})
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'Segoe UI', sans-serif;
}
nav {
position: fixed;
width: 100%;
height: 50px;
background: #222;
top: 0;
left: 0;
color: white;
display: flex;
}
nav>* {
flex: 1;
}
#logo {
padding-left: 20px;
font-size: 30px;
text-transform: uppercase;
letter-spacing: 2px;
display: flex;
justify-content: flex-start;
align-items: center;
}
nav ul {
display: flex;
list-style: none;
height: 100%;
}
nav ul li {
position: relative;
flex: 1;
background: #222;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: 0.2s;
}
nav ul li:hover {
background: #555;
}
.project-links {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
left: 0;
top: 100%;
width: 100%;
background-color: white;
color: black;
/* This is the height of this div + height of the nav bar */
transform: translateY(-135%);
transition: 0.2s;
z-index: -1;
}
.project-links.open {
transform: translateY(0);
}
.project-link {
height: 50px;
display: flex;
align-items: center;
padding-left: 20px;
text-decoration: none;
color: white;
cursor: pointer;
transition: 0.2s;
background: #222;
color: white;
}
.project-link:hover {
background: #555;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<nav>
<div id="logo">Logo</div>
<ul>
<li>About</li>
<li id="projects">
<span class="tab-name">Projects</span>
<div class="project-links">
<a class="project-link" href="#"><i class="fa fa-envelope"></i> Link 1</a>
<a class="project-link" href="#"><i class="fa fa-envelope"></i> Link 2</a>
<a class="project-link" href="#"><i class="fa fa-envelope"></i> Link 3</a>
</div>
</li>
<li>Contact</li>
</ul>
</nav>
To close the dropdown when the user clicks elsewhere, add an event listener for click and check whether the target is a subchild of projectsTab.

Since your dropdown menu elements are already flex containers, you can simply add an image at the beginning of each of them, give them a class (icon) and change a bit the padding. Here I put icon { padding: 3px; } and I removed the 20px padding at the beginning of menu elements, because it squished the images against the text.
I also added some JavaScript to close the menu when you click elsewhere.
const projectsTab = document.getElementById('projects')
const tabName = projectsTab.querySelector('.tab-name')
const projectLinks = document.querySelector('.project-links')
projectsTab.addEventListener('click', e => {
const isOpen = projectLinks.classList.contains('open')
if (isOpen) projectLinks.classList.remove('open')
else projectLinks.classList.add('open')
})
addEventListener('click', e => {
var target = e.target;
if (!(target.classList.contains('project-link') || target.classList.contains('project-link') || target.classList.contains('tab-name') || target.id == "projects")) {
projectLinks.classList.remove('open');
} else {
e.stopPropagation();
}
});
// link event listeners
const links = [...projectLinks.children] // turn this into an array
links.forEach(link => link.addEventListener('click', e => {
tabName.innerText = link.innerText
}))
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'Segoe UI', sans-serif;
}
nav {
position: fixed;
width: 100%;
height: 50px;
background: #222;
top: 0;
left: 0;
color: white;
display: flex;
}
nav>* {
flex: 1;
}
#logo {
padding-left: 20px;
font-size: 30px;
text-transform: uppercase;
letter-spacing: 2px;
display: flex;
justify-content: flex-start;
align-items: center;
}
nav ul {
display: flex;
list-style: none;
height: 100%;
}
nav ul li {
position: relative;
flex: 1;
background: #222;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: 0.2s;
}
nav ul li:hover {
background: #555;
}
.project-links {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
left: 0;
top: 100%;
width: 100%;
background-color: white;
color: black;
/* This is the height of this div + height of the nav bar */
transform: translateY(-135%);
transition: 0.2s;
z-index: -1;
}
.project-links.open {
transform: translateY(0);
}
.project-link {
height: 50px;
display: flex;
align-items: center;
padding-left: 5px;
text-decoration: none;
color: white;
cursor: pointer;
transition: 0.2s;
background: #222;
color: white;
}
.project-link:hover {
background: #555;
}
.icon {
padding-right: 3px;
}
<nav>
<div id="logo">Logo</div>
<ul>
<li>About</li>
<li id="projects">
<span class="tab-name">Projects</span>
<div class="project-links">
<a class="project-link" href="#"><img class="icon" src="http://lorempixel.com/20/20/cats/">Link 1</a>
<a class="project-link" href="#"><img class="icon" src="http://lorempixel.com/20/20/cats/">Link 2</a>
<a class="project-link" href="#"><img class="icon" src="http://lorempixel.com/20/20/cats/">Link 3</a>
</div>
</li>
<li>Contact</li>
</ul>
</nav>

This is actually pretty easy just like the example you linked to first add the link to the icon library inside the <head></head> tag of your html:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
then add the <i/> tag inside the <a></a> tag of your drop down. Your html code should look like so
<nav>
<div id="logo">Logo</div>
<ul>
<li>About</li>
<li id="projects">
<span class="tab-name">Projects</span>
<div class="project-links">
<a class="project-link" href="#"><i class="fa fa-home"/>Link 1</a>
<a class="project-link" href="#"><i class="fa fa-search"/>Link 2</a>
<a class="project-link" href="#"><i class="fa fa-globe"/>Link 3</a>
</div>
</li>
<li>Contact</li>
</ul>
</nav>

Related

How to ensure only 1 nav bar is dropped when clicking on specific element?

I have this situation where I am clicking on a button in my nav bar and would like the corresponding nav bar to drop, I've drafted the HTML and attempted to create the JavaScript for this but haven't been able to successfully implement it.
const dropdownButtons = document.querySelectorAll(".dropdownButton");
const dropdownNav = document.querySelectorAll(".dropdown");
const navBar = document.querySelector("#navBar");
function toggleDropdown() {
dropdownNav.forEach((x => x.classList.toggle("show")))
}
dropdownButtons.forEach((btn) => {
btn.addEventListener("click", (e) => {
const button = e.target;
toggleDropdown(button)
})
})
#import url('https://fonts.googleapis.com/css2?family=Overpass:wght#300;600&family=Ubuntu:wght#400;500;700&display=swap');
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'Overpass', sans-serif;
font-family: 'Ubuntu', sans-serif;
}
:root {
--dark-red-cta: hsl(356, 100%, 66%);
--light-red-cta: hsl(355, 100%, 74%);
--dark-blue-heading: hsl(208, 49%, 24%);
--white: hsl(0, 0%, 100%);
--grayish-blue-footer: hsl(240, 2%, 79%);
--dark-grayish-blue: hsl(207, 13%, 34%);
--very-dark-blue: hsl(240, 10%, 16%);
}
/* NAVBAR */
nav {
padding: 2em 10em;
}
nav img {
padding-right: 2em;
}
nav,
.siteNav {
display: flex;
align-items: center;
justify-content: space-between;
}
ul {
list-style-type: none;
}
li {
display: inline;
}
button img {
margin-left: .5em;
}
button {
cursor: pointer;
background: none;
border: none;
color: white;
font-size: .9em;
}
header {
text-align: center;
background-image: url(/images/bg-pattern-intro-desktop.svg), linear-gradient(100deg, hsl(13, 100%, 72%), hsl(353, 100%, 62%));
background-size: 218%, auto;
background-position: 25% 52%;
height: 75vh;
border-radius: 0 0 0 6em;
}
header h1 {
color: white;
font-size: 3em;
font-weight: 500;
padding: 2em 0 .5em 0;
}
header p {
color: white;
padding: 0 0 2em 0;
}
.startButton,
.loginButton {
background-color: white;
color: var(--dark-red-cta);
border-radius: 30px;
width: 140px;
padding: 1.1em;
font-weight: bold;
}
.learnMoreButton {
color: white;
border-radius: 30px;
width: 140px;
padding: 1.1em;
border: 1px solid var(--white);
font-weight: bold;
}
/* For the drop down */
.dropdownItem {
position: relative;
}
.dropdown {
position: absolute;
background: var(--white);
border-radius: 8px;
padding: 1em;
text-align: left;
margin-top: 1em;
height: 14em;
width: 10em;
display: flex;
flex-direction: column;
justify-content: space-evenly;
visibility: hidden;
}
.show {
visibility: visible;
}
.rotateImg {
transform: rotate(90deg);
}
.dropdown li a {
color: black;
text-decoration: none;
}
.dropdown li a:hover {
font-weight: bold;
}
<nav>
<div class="siteNav">
<img src="/images/logo.svg" alt="Blogr logo">
<ul id="navBar">
<li class="dropdownItem">
<button class="dropdownButton">Product
<img src="/images/icon-arrow-light.svg" alt="Drop down arrow">
</button>
<ul class="dropdown dropdown-content">
<li>Overview</li>
<li>Pricing</li>
<li>Marketplace</li>
<li>Features</li>
<li>Integrations</li>
</ul>
</li>
<li class="dropdownItem">
<button class="dropdownButton">Company
<img src="/images/icon-arrow-light.svg" alt="Drop down arrow">
</button>
<ul class="dropdown dropdown-content">
<li>About</li>
<li>Team</li>
<li>Our Blog</li>
<li>Careers</li>
</ul>
</li>
<li class="dropdownItem">
<button class="dropdownButton">Connect
<img src="/images/icon-arrow-light.svg" alt="Drop down arrow">
</button>
<ul class="dropdown dropdown-content">
<li>Contact</li>
<li>Newsletter</li>
<li>LinkedIn</li>
</ul>
</li>
</ul>
</div>
<div class="login">
<button class="loginButton">Login</button>
<button class="signUpButton">Sign Up</button>
</div>
</nav>
I'm currently successfully able to correspond a click and assign the correct class using this method but the only issue is that it adds it to all 3 drop downs and I just cannot wrap my head around event bubbling so would appreciate a little bit of hand holding for this example.
I am looping through the buttons and then triggering a function which should then make the relative drop down visible by updating it's visibility. This works and it only shows the 3rd option because it updates all 3 classes instead of one.
I tried using a current target approach on this but wasn't able to get it to work since I couldn't add a classList to e.target. Could someone point out to me where I'm going wrong on this one?
EDIT:
Added CSS file as it would be relevant to see the positioning of the elements understandably.
Pass a prop index so that you would toggle the class .show only to that particular dropdown and not all
const dropdownButtons = document.querySelectorAll(".dropdownButton");
const dropdownNav = document.querySelectorAll(".dropdown");
const navBar = document.querySelector("#navBar");
function toggleDropdown(ind) {
dropdownNav[ind].classList.toggle("show")
}
dropdownButtons.forEach((btn, index) => {
btn.addEventListener("click", (e) => {
const button = e.target;
toggleDropdown(index)
})
})

Is there a solution for this button problem?

I am building up my skills by designing a mini web pag
so I started the navigation bar .
The button "Template" is supposed to change the view of the and display the lists but when I click it nothing happens.
Can anyone help me ?
The code:
html:
<div class="menu-toggle">
<h1 id="logo">PROTOCOL</h1>
<li class="right table" id="table"><a class="list template" href="#template" >☰</a></li>
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
</div>
<ul id="navigate" class="navigate">
<li><a class="list home" href="#home"> Home</a></li>
<li><a class="list" href="#conntacting">Contact us</a></li>
<li class="right"><a class="list sign-up" href="#signing">Sign up</a></li>
<li class="right"><a class="list" href="#logging">Log in</a></li>
</ul>
</nav>
CSS:
I designed my navigation bar to be displayed when the user press the "template" button
and hide it again by pressing the same button.
#media screen and ( max-width : 675px){
.container{
position: relative;
}
.menu-toggle{
text-align: center;
float: none;
display: block;
}
.template{
display: inline;
float: right;
padding: 0 40px;
margin: auto;
}
li.table{
left: 80px;
height: 20px;
}
.nav{
height: 90px;
}
.template:hover{
display: inline;
float: right;
padding: 0 40px;
margin: auto;
}
.navigate,.active{
text-align: center;
display: grid;
grid-template-columns: auto;
background-color: #b00000;
margin: 0;
width: 100%;
position: absolute;
top:90px;
transition: all 0.5 ease;
left: 100%;
}
li.right{
display: table;
padding: 0;
}
.menu-toggle.bar{
width: 25px;
height: 3px;
margin: 5px auto;
transition: all 0.3s ease-in-out;
box-sizing: border-box;
}
.active{
text-align: center;
display: grid;
grid-template-columns: auto;
background-color: #b00000;
margin: 0;
width: 100%;
position: absolute;
top:90px;
transition: all 0.5 ease;
}
}
JavaScript:
I brought the button "template" and "ul" by it ids
var template = document.getElementById('template');
var nav = document.getElementById('navigate');
template.onclick = function () {
if (nav.className === ('navigate')) {
nav.className += 'active';
} else {
nav.className = 'navigate';
};
You can use classList.toggle method.
Also, to tidy up things, I've removed the list buttons, changed menu font color to white, and removed the duplicated CSS on classes navigate and active: now .active just displays the navigation menu.
(The navigation menu was exploding the width when opened, and it was clobbering the menu icon, so I just removed position: absolute; and width: 100% from it)
Far from done here. Try to change things in steps, and to study the css rules on MDN, instead of doing many things at once.
var template = document.querySelector('.template');
var nav = document.querySelector('.navigate');
template.onclick = function() {
nav.classList.toggle('active');
};
#media screen and ( max-width: 675px) {
.container {
position: relative;
}
.menu-toggle {
text-align: center;
float: none;
display: block;
}
.template {
display: inline;
float: right;
padding: 0 40px;
margin: auto;
}
li.table {
left: 80px;
height: 20px;
}
.nav {
height: 90px;
}
.template:hover {
display: inline;
float: right;
padding: 0 40px;
margin: auto;
}
.navigate {
text-align: center;
display: none;
background-color: #b00000;
list-style: none;
margin: 0;
left: 0;
top: 90px;
transition: all 0.5 ease;
}
a.list:not(.template) {
text-decoration: none;
color: white;
}
li.right {
display: table;
padding: 0;
}
.menu-toggle.bar {
width: 25px;
height: 3px;
margin: 5px auto;
transition: all 0.3s ease-in-out;
box-sizing: border-box;
}
.active {
display: grid;
grid-template-columns: auto;
}
<div class="menu-toggle">
<h1 id="logo">PROTOCOL</h1>
<li class="right table" id="table"><a class="list template" href="#template">☰</a></li>
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
</div>
<nav id="navigate">
<ul class="navigate">
<li><a class="list home" href="#home"> Home</a></li>
<li><a class="list" href="#contacting">Contact us</a></li>
<li class="right"><a class="list sign-up" href="#signing">Sign up</a></li>
<li class="right"><a class="list" href="#logging">Log in</a></li>
</ul>
</nav>
your code has many mistakes please create button having template class.
however i updated your javascript for current use
template.onclick = function () {
if (nav.className === 'navigate') {
nav.classList.remove('navigate')
nav.className = 'active';
}
else {
nav.classList.remove('active')
nav.className = 'navigate';
}
}
}

scroll animation with html # tags

I currently am trying to code a website to animate moving across the x-axis to access different sections of the page (page content in 'tab-content'). I have a navbar that has different headers, this is fixed, I want the user to click on each header and be taken to that section. I managed to take the user to the desired section/div with some JS code however, there isn't any animation it defaults to the selected section/div just suddenly being on screen. How do I animate with pure JS or CSS. I need the clicking of the header to move (motion) the user to that div. I'm new to web dev.
here some of my code
HTML
<div class="main-info">
<div class="nav-container">
<div class="nav-bar">
<ul>
<li data-tab-target="#show" class="tab">Show</li>
<li data-tab-target="#about" class="tab">About</li>
<li data-tab-target="#lookbook" class="tab">Lookbook</li>
<li data-tab-target="#process" class="tab">Process</li>
</ul>
</div>
<div class="info overlay">
<div class="text">
MA
Coming Soon
BA
</div>
Back
</div>
</div>
<div class="tab-content">
<div id="show" data-tab-content class="active">
<p>VIDEO</p>
</div>
<div id="about" data-tab-content>
<p>About</p>
</div>
<div id="lookbook" data-tab-content>
<p>Lookbook</p>
</div>
<div id="process" data-tab-content>
<p>Process</p>
</div>
</div>
</div>
CSS
.main-info {
background-color: transparent;
height: 100vh;
overflow: hidden;
}
.nav-container {
position: fixed;
}
.nav-bar {
width: 80vw;
height: 10vh;
left: 10vw;
position: absolute;
top: 5vh;
}
.nav-bar ul {
text-transform: uppercase;
list-style: none;
margin: 0;
padding: 0;
display: flex;
justify-content: space-around;
}
.tab a {
font-family: Helvetica, sans-serif;
font-weight: bold;
font-size: 1rem;
color: black;
text-decoration: none;
}
.tab:hover {
cursor: pointer;
opacity: 0.6;
}
.tab.active {
background-color: whitesmoke;
}
.info {
width: 90vw;
height: 10vh;
/* border: 1px solid red; */
left: 5vw;
position: absolute;
top: 80vh;
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.info a {
font-family: Helvetica, sans-serif;
font-weight: bold;
font-size: 1.1rem;
color: black;
text-decoration: none;
border: 1px solid teal;
}
.text {
width: 30%;
display: flex;
justify-content: space-between;
}
.tab-content {
border: 1px solid teal;
position: absolute;
left: 0;
top: 0;
height: 100vh;
z-index: -11;
display: flex;
flex: row nowrap;
justify-content: flex-start;
}
[data-tab-content] {
border: 1px solid blueviolet;
background-color: violet;
font-size: 3rem;
color: blue;
scroll-behavior: smooth;
display: none;
width: 100vw;
height: 100vh;
}
.active[data-tab-content] {
display: block;
}
JS
const tabs = document.querySelectorAll('[data-tab-target]');
const tabContents = document.querySelectorAll('[data-tab-content]')
// loop through the list to find the one tab mouse clicked
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const target = document.querySelector(tab.dataset.tabTarget)
tabContents.forEach(tabContent => {
tabContent.classList.remove('active')
})
tabs.forEach(tab => {
tab.classList.remove('active')
});
tab.classList.add('active')
target.classList.add('active');
});
});
You almost got it. Instead of setting the scroll-behavior on the elements that are inside a scrollable element, put it on either the element that has a scrollbar.
.tab-content {
scroll-behavior: smooth;
}
Or on the top most element to have all elements move with a smooth scrolling animation.
:root {
scroll-behavior: smooth;
}

Javascript event listener overwrites another event listener

so I am trying to create a multilevel navigation for a mobile device and I am running into some issues. I am still learning javascript so please bare with me. Here is a link to the codepen:
https://codepen.io/maciekmat/pen/yLepYKq
So when you press the menu in the top right, a menu will open down. Then I would like to be able to go into sub categories. For example click Test Open, .active class will be assigned, and another menu slides in. Now I would like to have a go back button that essentially removes the .active class.
However what I think is happening, the event listener listens to the whole parent, and anywhere you click inside the subnavigation, it registers it as a click, and runs the .active class. When I click to go back, its like .active is removed and applied back instantly. Any help please?
I tried doing the event.currentEvent !== event.target if statement but had no luck
const nav = document.getElementById('menuIcon')
const dropdown = document.getElementById('menuDropdown')
nav.addEventListener('click', function() {
dropdown.classList.toggle('nav-is-toggled')
});
const grabNavLinks = document.querySelectorAll('.sub-nav-link');
const grabBackLinks = document.querySelectorAll('.nav-link.back');
const subNavLinks = Array.from(grabNavLinks);
const backLinks = Array.from(grabBackLinks);
for (let i = 0; i < subNavLinks.length; i++) {
subNavLinks[i].addEventListener("click", function() {
this.querySelector('.sub-nav').classList.add('active');
});
}
for (let i = 0; i < backLinks.length; i++) {
backLinks[i].addEventListener("click", function() {
document.querySelector('.sub-nav').classList.remove('active');
});
}
#import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght#300;400;600;700&display=swap");
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: "Open sans", sans-serif;
background: #F9F9F9;
box-sizing: border-box;
}
a, p, h1, h2, h3 {
margin: 0;
padding: 0;
text-decoration: none;
}
ul, li {
padding: 0;
margin: 0;
}
header {
background: white;
padding: 24px 30px;
display: flex;
flex-direction: column;
z-index: 10;
position: relative;
}
.top-nav {
display: flex;
justify-content: space-between;
align-items: center;
}
.top-nav .logo {
background: url("images/tcb-logo-brand.svg") no-repeat;
width: 150px;
height: 50px;
background-size: contain;
background-position: center;
}
.top-nav .navigation {
display: flex;
justify-content: space-between;
width: 132px;
height: 26px;
}
.top-nav .navigation .nav-item {
width: 23px;
height: 26px;
margin-left: 10px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
.top-nav .navigation .nav-item.menu {
background-image: url("images/menu-icon.svg");
}
.top-nav .navigation .nav-item.notification {
background-image: url("images/bell-icon.svg");
}
.top-nav .navigation .nav-item.my-account {
background-image: url("images/acc-icon.svg");
}
.search {
margin-top: 16px;
}
.search input[type=search] {
outline: none;
border: none;
background: #F6F6F6;
width: 100%;
height: 50px;
font-size: 16px;
padding: 10px 0 10px 20px;
background-image: url("images/search-icon.svg");
background-repeat: no-repeat;
background-position: right 15px top 50%;
}
.search input[type=search]::placeholder {
color: #B7B7B7;
}
span.nav-title {
display: none;
}
nav.main-nav {
width: 100%;
background: #F6F6F6;
transform: translateY(-100%);
z-index: 0;
position: relative;
transition: all 0.3s ease-in-out;
position: absolute;
padding-bottom: 50px;
overflow: hidden;
}
nav.main-nav ul {
padding-top: 2px;
position: relative;
}
nav.main-nav li {
list-style: none;
}
nav.main-nav a {
color: #6D6D6D;
font-size: 16px;
width: 100%;
height: 100%;
display: block;
padding: 18px 30px;
border-bottom: 1px solid #E9E9E9;
}
nav.main-nav li.nav-link.arrow {
background: url("images/right-arrow.svg") no-repeat;
background-position: right 30px top 50%;
}
nav.main-nav ul.sub-nav {
background: #cecece;
width: 100%;
display: flex;
flex-direction: column;
transform: translateX(100%);
overflow: hidden;
visibility: hidden;
opacity: 0;
position: absolute;
top: 0;
left: 0;
transition: transform 0.2s ease;
}
.sub-nav-link > .sub-nav.active {
transform: translateX(0);
visibility: visible;
opacity: 1;
}
nav.nav-is-toggled {
position: static;
opacity: 1;
transform: translateY(0);
transition: all 0.3s ease-in-out;
}
<header>
<div class="top-nav">
<div class="logo"></div>
<div class="navigation">
<div id="menuIcon" class="nav-item menu">test</div>
<div class="nav-item notification"></div>
<div class="nav-item my-account"><span class="nav-title">My Account</span></div>
</div>
</div>
<div class="search">
<input type="search" name="search" id="search" placeholder="Search by store...">
</div>
</header>
<nav id="menuDropdown" class="main-nav">
<ul class="main-nav-ul">
<li class="nav-link">Test</li>
<li class="nav-link arrow sub-nav-link">Test Open
<ul class="sub-nav">
<li class="nav-link back">Go Back</li>
<li class="nav-link">Test</li>
<li class="nav-link">Test</li>
<li class="nav-link">Test</li>
</ul>
</li>
<li class="nav-link">Test</li>
<li class="nav-link">Test</li>
<li class="nav-link arrow sub-nav-link">Test Open
<ul class="sub-nav">
<li class="nav-link back">Go Back</li>
<li class="nav-link">Test</li>
<li class="nav-link">Test</li>
</ul>
</li>
<li class="nav-link">Test</li>
</ul>
</nav>
<script src="navigation.js"></script>
Go read on event bubbling and how it bubbles through the DOM tree. However due to the way it passes from parent to child (that's it bubbles down the tree) you should call event.stopPropagation(); So that the previous event doesnt bubble into the backLinks event listener.
This is what you should do
for (let i = 0; i < backLinks.length; i++) {
backLinks[i].addEventListener("click", function(event) {
event.stopPropagation();
document.querySelector('.sub-nav').classList.remove('active');
});
}

Why does my div still display when its height is set to 0?

I understand this is probably a question that has been asked before, but I haven't found a post or another question that has solved this issue.
I want to make a drop down mobile menu with Javascript by toggling the height of the #mobileMenu div. I wanted the div to have an initial height of 0 when the document loads, and add its full height when the triggering button is clicked. The only issue is when I set the div's initial height to 0, the document still displays the div with a height of 27.59px which doesn't make much sense to me.
I've tried adding: overflow: hidden; / line-height: 0; / display: block but no matter what I do, the div will not go below 27.59px in height. I even completed the Javascript functionality and the div will open to 154px in height, but when closed it goes back to 27.59px instead of 0.
const openBtn = document.querySelector('.open');
const closeBtn = document.querySelector('.close');
const mobileMenu = document.getElementById('mobileMenu');
openBtn.addEventListener('click', e => {
mobileMenu.classList.remove('hidden');
mobileMenu.classList.add('active');
openBtn.style.display = 'none';
closeBtn.style.display = 'block';
});
closeBtn.addEventListener('click', e => {
mobileMenu.classList.remove('active');
mobileMenu.classList.add('hidden');
openBtn.style.display = 'block';
closeBtn.style.display = 'none';
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
position: relative;
}
/* Header */
header {
display: flex;
justify-content: space-around;
align-items: center;
padding: .5rem;
height: 60px;
position: fixed;
top: 0;
left: 0;
right: 0;
}
header h2 {
font-family: 'Calistoga';
letter-spacing: 2px;
}
header i {
font-size: 1.5rem;
}
header i:hover {
cursor: pointer;
}
header i.close {
display: none;
}
/* Mobile Nav */
#mobileMenu {
padding: .8rem 0;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
position: fixed;
top: 92px;
left: 0;
right: 0;
overflow: hidden;
transition: height .7s ease-in-out;
}
#mobileMenu.hidden {
height: 0;
line-height: 0;
display: block;
}
#mobileMenu.active {
height: 154px;
}
.mobile-nav {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
list-style: none;
}
.mobile-nav li {
padding: .3rem 0;
}
.mobile-nav li a {
text-decoration: none;
font-size: 1.2rem;
color: #000;
}
<header>
<h2>Website Header</h2>
<i class="fas fa-chevron-down open"></i>
<i class="fas fa-chevron-up close"></i>
</header>
<div id="mobileMenu" class="hidden">
<ul class="mobile-nav">
<li>
Home
</li>
<li>
Services
</li>
<li>
About
</li>
<li>
Contact
</li>
</ul>
</div>
With or without the overflow: hidden; / line-height: 0; / display: block the result remains the same.
Any help would be much appreciated. Thank you for your time.
Try to set hidden property on mobileMenu div, and update accordingly on button click. This way you avoid messing with css classes
const openBtn = document.querySelector('.open');
const closeBtn = document.querySelector('.close');
const mobileMenu = document.getElementById('mobileMenu');
openBtn.addEventListener('click', e => {
mobileMenu.hidden = false;
//mobileMenu.classList.add('active');
openBtn.style.display = 'none';
closeBtn.style.display = 'block';
});
closeBtn.addEventListener('click', e => {
//mobileMenu.classList.remove('active');
mobileMenu.hidden = true;
openBtn.style.display = 'block';
closeBtn.style.display = 'none';
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
position: relative;
}
/* Header */
header {
display: flex;
justify-content: space-around;
align-items: center;
padding: .5rem;
height: 60px;
position: fixed;
top: 0;
left: 0;
right: 0;
}
header h2 {
font-family: 'Calistoga';
letter-spacing: 2px;
}
header i {
font-size: 1.5rem;
}
header i:hover {
cursor: pointer;
}
header i.close {
display: none;
}
/* Mobile Nav */
#mobileMenu {
padding: .8rem 0;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
position: fixed;
top: 92px;
left: 0;
right: 0;
overflow: hidden;
transition: height .7s ease-in-out;
}
#mobileMenu.hidden {
height: 0;
line-height: 0;
display: block;
}
#mobileMenu.active {
height: 154px;
}
.mobile-nav {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
list-style: none;
}
.mobile-nav li {
padding: .3rem 0;
}
.mobile-nav li a {
text-decoration: none;
font-size: 1.2rem;
color: #000;
}
<header>
<h2>Website Header</h2>
<i class="fas fa-chevron-down open"></i>
<i class="fas fa-chevron-up close"></i>
</header>
<div id="mobileMenu" hidden>
<ul class="mobile-nav">
<li>
Home
</li>
<li>
Services
</li>
<li>
About
</li>
<li>
Contact
</li>
</ul>
</div>
How about making the default for the menu your hidden state and removing the hidden class. Block is the default display for a div so that's not needed. Try setting padding on #mobileMenu to 0, and setting margin on .mobile-nav to .8rem 0
Using your logic but with this only change will fix it:
#mobileMenu.hidden {
height: 0;
+ padding: 0; /*the initial padding: .8rem 0; creates the 27.59px height (0.8rem top + 0.8rem bottom)*/
line-height: 0;
display: block;
}

Categories