How to hide navigation bar on link click? - javascript

I have 3 buttons and a responsive hamburger menu. Everything works as expected, but I can't think of a way to make a navigation bar go away as soon as I click on a button.
The program is supposed to work like this: clicking hamburger menu activates 3 buttons, whenever user clicks on any of those 3 buttons it hides the buttons and only leaves the button that was clicked.
This is the wanted outcome:
This is my code so far.
html:
<div class="selectSection">
<button type="button" data-number="1" class="active">1</button>
<button type="button" data-number="2">2</button>
<button type="button" data-number="3">3</button>
</div>
<div class="hamburger">
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
</div>
<div>
<div class="content" data-number="1">
<p>1st page</p>
</div>
<div class="content" data-number="2">
<p>2nd page</p>
</div>
<div class="content" data-number="3">
<p>3rd page</p>
</div>
</div>
css
.content:not(:first-child) {
display: none;
}
.active {
color: orange !important;
}
.hamburger {
display: none;
}
#media all and (max-width: 800px) {
.hamburger {
display: inline-block;
cursor: pointer;
z-index: 7;
}
.hamburger .line {
width: 30px;
height: 3px;
background: black;
margin: 6px 0px;
}
.selectSection {
display: none;
overflow: hidden;
}
.selectSection.active {
display: block;
}
}
js
// change active class, show the clicked element only and hide the others
// grab all the buttons
let Buttons = document.querySelectorAll(".selectSection button");
// loop through the buttons using for..of
for (let button of Buttons) {
// listen for a click event
button.addEventListener("click", (e) => {
// et = event target
const et = e.target;
// slect active class
const active = document.querySelector(".active");
// check for the button that has active class and remove it
if (active) {
active.classList.remove("active");
}
// add active class to the clicked element
et.classList.add("active");
// select all classes with the name content
let allContent = document.querySelectorAll(".content");
// loop through all content classes
for (let content of allContent) {
// display the content if the class has the same data-attribute as the button
if (
content.getAttribute("data-number") ===
button.getAttribute("data-number")
) {
content.style.display = "block";
}
// if it's not equal then hide it.
else {
content.style.display = "none";
}
}
});
}
hamburger = document.querySelector(".hamburger");
hamburger.onclick = function () {
navBar = document.querySelector(".selectSection");
navBar.classList.toggle("activate");
};
This is the demo:
https://codepen.io/f4kermak3r/pen/ExRPKzJ

you are using the wrong css class in your js file. At line 44, you must change navBar.classList.toggle("activate") to navBar.classList.toggle("active"). That should work.

Related

TypeError: Cannot read properties of null (reading 'classList')

I am working in a nextjs application. I was just trying to make a dropdown.
This is my full code:
import React from 'react'
import Link from 'next/link'
import styles from './header.module.css'
const Header = () => {
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
const myFunction =()=>{
document.getElementById(styles.myDropdown).classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
if (typeof window !== "undefined") {
window.onclick = function (event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
}
return (
<div className={styles.header}>
<div className={styles.logoLink}>
<img src="images/itacs.png" alt="" className={styles.logo} />
</div>
<div className={styles.services}>
<ul>
<li><Link href="/page">Docs</Link></li>
<li><Link href="/page">Learn</Link></li>
<li><Link href="/page">Projects</Link></li>
<li><Link href="/page">Blog</Link></li>
<div className={styles.dropdown}>
<button onClick={myFunction} className={styles.dropbtn}>Dropdown</button>
<div id={styles.myDropdown} className={styles.dropdownContent}>
Link 1
Link 2
Link 3
</div>
</div>
</ul>
</div>
<form action="" className={styles.headerForm}>
<a href="/" className={styles.logIn}>Log In</a>
<a href="/" className={styles.getStarted}>Get Started</a>
</form>
</div>
)
}
export default Header
Here I have just added the classlist in the id of div!
I am trying to show the below div when the button is clicked as a dropdown menu.
I am not able to figure this out!
For anyone who is wondering what is there present in css file :
/* dropdown */
/* Dropdown Button */
.dropbtn {
background-color: #3498DB;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
}
/* Dropdown button on hover & focus */
.dropbtn:hover, .dropbtn:focus {
background-color: #2980B9;
}
/* The container <div> - needed to position the dropdown content */
.dropdown {
position: relative;
display: inline-block;
}
/* Dropdown Content (Hidden by Default) */
.dropdownContent {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
/* Links inside the dropdown */
.dropdownContent a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
/* Change color of dropdown links on hover */
.dropdownContent a:hover {background-color: #ddd}
/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */
.show {display:block;}
Any Help would be appreciated!
It should be <div className="dropdown"> instead of <div class="dropdown"> and <div id={styles.myDropdown} className={styles.dropdownContent}>
You would also avoid doing vanilla js inside a react/next app.
You would instead have a react state like this:
const [dropdownToggled, toggleDropdown] = useState(false);
const handleClick = () => {
toggleDropdown(!dropdownToggled);
};
And have a condition on whether your jsx have a className hidden that sets display: none
something like this:
<div
className={`${styles.dropdownContent}
${dropdownToggled ? styles.hidden : ""}`}
>
Link 1
Link 2
Link 3
</div>
To make the dropdown close when the user clicks outside, you would have a div like this:
<div
className={styles.backdrop}
onClick={() => toggleDropdown(true)}
></div>
that is styled like this:
.backdrop {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -1;
}
Now since this div takes the whole screen and is positioned absolute when the user clicks anywhere on the page the onClick will fire and toggle the dropdown.
Working CodeSandbox.
import React, { useState } from 'react'
import Link from 'next/link'
import styles from './header.module.css'
const Header = () => {
const [dropdownToggled, toggleDropdown] = useState(false);
const handleClick = () => {
toggleDropdown(!dropdownToggled);
console.log("boy");
};
// Close the dropdown menu if the user clicks outside of it
if (typeof window !== "undefined") {
window.onclick = function (event) {
if (!event.target.matches('#dropbtn')) {
var dropdowns = document.getElementsByClassName(styles.dropdownContent);
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
}
return (
<div className={styles.header}>
<div className={styles.logoLink}>
<img src="images/itacs.png" alt="" className={styles.logo} />
</div>
<div className={styles.services}>
<ul>
<li><Link href="/page">Docs</Link></li>
<li><Link href="/page">Learn</Link></li>
<li><Link href="/page">Projects</Link></li>
<li><Link href="/page">Blog</Link></li>
<div className={styles.dropdown}>
<button onClick={handleClick} id="dropbtn" className={styles.dropbtn}>Dropdown</button>
<div
className={`${styles.dropdownContent} ${dropdownToggled ? styles.show : ""}`}
>
Link 1
Link 2
Link 3
</div>
</div>
</ul>
</div>
<form action="" className={styles.headerForm}>
<a href="/" className={styles.logIn}>Log In</a>
<a href="/" className={styles.getStarted}>Get Started</a>
</form>
</div>
)
}
export default Header
Yea finally configured it out. Thanks to whygee and Ingo Steinkie
If you use variables to set the id and className in the markup, you should probably use the same variables in your script, like
document.getElementById(`${styles.myDropdown}`).classList.toggle("show");
[Edit] or without the unnecessary redundant template string:
document.getElementById(styles.myDropdown).classList.toggle("show");
Assuming that styles.myDropdown is a string (not an object).
Otherwise your code does not ensure that the id and className will be the same.

How do I hide a modal when the overlay is clicked in vanilla javascript

I'm trying to figure out how to hide the div which is the overlay only when the overlay is clicked and not any of its children (ie: the content):
modal.addEventListener('click', destroy, false);
function destroy(e) {
if (e.target.id === overlay.id) {
window.location.reload(false);
}
}
Your if condition is working but you don't hide something. For that you could add a class (for example .hidden) and define for that class display: none.
Working example:
const modal = document.querySelector('#modal');
const overlay = document.querySelector('#overlay');
modal.addEventListener('click', destroy, false);
function destroy(e) {
if (e.target.id === overlay.id) {
e.target.classList.add('hidden');
}
}
#overlay {
height: 50px;
background-color: yellow;
}
p {
background-color: red;
}
.hidden {
display: none;
}
<div id="modal">
<div id="overlay">
<p>test</p>
</div>
</div>

Nested element in button element are not clickable

I am having this problem, I created a button, and inside I have a for Icon, and inside the element I have span to style the text next to the Icon (the Icon from is humberger from awesome font)
the issue is:
in javascript, I created an onclick function for the button element using the ID btnm, but when I click on the text or the icon in the button does work though when I click around the text and the icon in the button the onclick works fine.
I cannot understand why the icon and text are in the button.
please help
document.addEventListener('DOMContentLoaded', function(){
var menubtn = document.getElementById('btnm');
var mobilemenu = document.getElementById('navigation-mobile');
// When the user clicks on the button, open the modal
menubtn.onclick = function() {
if (mobilemenu.style.display == 'block') {
mobilemenu.style.display = "none";
}
else {
mobilemenu.style.display = 'block';
}
}
}
.mobile-menu-btn {
float: right;
display: block;
padding: 3px 3px 0px 0px;
}
.humberger {
background-color: $identity-color;
font-size: 20px;
border: $identity-color;
border: none;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
.menu-pargraph {
font-size: 14px;
vertical-align: middle;
padding-left: 5px;
margin: 0;
width: 100%;
}
<div class ="mobile-menu-btn">
<button class="humberger" id="btnm">
<i class="menu-btn fas fa-bars">
<span class="menu-pargraph">Menu</span>
</i>
</button>
</div>
<div id="navigation-mobile">
<ul>
<li>home</li>
<li>video</li>
<li>contact</li>
</ul>
</div>
Explanations in comments below. You had the id (and click listener) on the div, not the button and your 'Menu' text was probably looking funky b/c it was inside the icon element, inheriting the icon font family.
<div class ="mobile-menu-btn">
<button class="humberger" id="btnm"> <!-- put the id here -->
<i class="menu-btn fas fa-bars"></i>
<span class="menu-pargraph">Menu</span> <!-- move outside of the fontawesome icon -->
</button>
</div>
Also you can make your life easier with the show/hide using a class
css:
#navigation-mobile{
display:none;
/* and whatever other styles you have here */
}
.show {
display:block;
}
then in your script:
menubtn.onclick = function() {
mobilemenu.classList.toggle('show');
}
You missed a ")" in your JS code.
document.addEventListener('DOMContentLoaded', function(){
var menubtn = document.getElementById('btnm');
var mobilemenu = document.getElementById('navigation-mobile');
// When the user clicks on the button, open the modal
console.log("ok")
}
) // Here you have to add parenthesis

Basic jquery toggleClass not working in es6 class

I have a menu that I want to show when an icon is clicked. I have the menu hidden by default when the page loads. I am using jQuerys toggleClass() method. When I click the icon the show class is never added to the .menu. The menu class highlights in the browser dev tools each time I click but no class is being added to it.
If I use the addClass() method then the class is added but I want to be able to toggle the show class so the menu is able to be shown and hidden while clicking the same icon/element.
HTML
<i class="fa fa-ellipsis-v"></i>
<div class="menu">
<div id="context-menu">
Item 1
Item 2
Item 3
Item 4
</div>
</div>
Menu.js
class Menu {
constructor() {
this.menu = document.querySelector('.menu');
this.ellipsis = document.querySelector('.fa.fa-ellipsis-v');
this.callEvent();
}
callEvent() {
this.ellipsis.addEventListener('click',this.showMenu.bind(this));
}
showMenu(e){
e.preventDefault();
$('.menu').toggleClass('show');
}
}
CSS
.menu {
display:none;
}
.show{
display:block;
}
This shouldn't be an answer but it really can't be a comment. I used your code, added a little css to make things viewable and guessed at the html (but that shouldn't make a difference). The code works as expected.
class Menu {
constructor() {
this.menu = document.querySelector('.menu');
this.ellipsis = document.querySelector('.fa.fa-ellipsis-v');
this.callEvent();
}
callEvent() {
this.ellipsis.addEventListener('click',this.showMenu.bind(this));
}
showMenu(e){
e.preventDefault();
$('.menu').toggleClass('show');
}
}
var menu = new Menu();
.menu {
display:none;
background-color: blue;
height: 100px;
width: 100px;
}
.show{
display:block;
}
.fa-ellipsis-v {
font-weight: bold;
font-size: 20px;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="menu"></div>
<span class="fa fa-ellipsis-v">...</span>
See below. I'm using display:flex instead of display:block for layout purposes.
class Menu {
constructor() {
this.menu = document.querySelector('.menu');
this.ellipsis = document.querySelector('.fa.fa-ellipsis-v');
this.callEvent();
}
callEvent() {
this.ellipsis.addEventListener('click', () => {
$('.menu').toggleClass('show');
});
}
}
const dummy = new Menu();
.menu {
display: none;
}
.show {
display: flex;
}
nav {
display: flex;
}
ul {
list-style: none;
margin: 0;
justify-content: space-around;
width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<nav>
<i class="fa fa-ellipsis-v"></i>
<ul class="menu">
<li>This</li>
<li>is</li>
<li>a</li>
<li>menu</li>
</ul>
</nav>

How to unclick other button using 1 button

I have 15 buttons, each button has content, but when I clicked button1 and followed by button 2, the button 1 is still open. How can I close the button 1 if I click button2?
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
div.panel {
position: absolute;
max-width: 0;
overflow: hidden;
opacity: 0;
}
div.panel.show {
opacity: 1;
max-width: 900px;
}
<button class="accordion">The Ball</button>
<div class="panel">
<h1>The Ball</h1>
</div>
<button class="accordion">The Cat</button>
<div class="panel">
<h1>The Cat</h1>
</div>
<button class="accordion">The Dog</button>
<div class="panel">
<h1>The Dog</h1>
</div>
You need to remove active and show classes from previous div before adding them to clicked div.
Your JS Code will be:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
var previous = document.querySelector(".active"); //select previous button
if (previous) { // check because when first time no button has active class
previous.classList.remove("active");
previous.nextElementSibling.classList.remove("show");
}
this.classList.add("active");
this.nextElementSibling.classList.add("show");
};
}
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
var previous = document.querySelector(".active");
if (previous) {
previous.classList.remove("active");
previous.nextElementSibling.classList.remove("show");
}
this.classList.add("active");
this.nextElementSibling.classList.add("show");
};
}
div.panel {
position: absolute;
max-width: 0;
overflow: hidden;
opacity: 0;
}
div.panel.show {
opacity: 1;
max-width: 900px;
}
<button class="accordion">The Ball</button>
<div class="panel">
<h1>The Ball</h1>
</div>
<button class="accordion">The dog</button>
<div class="panel">
<h1>The dog</h1>
</div>
<button class="accordion">The cat</button>
<div class="panel">
<h1>The cat</h1>
</div>
So select the active one. If it is active remove it. If the current one is not the active class add it.
// select all the buttons
var btns = document.querySelectorAll('.accordion')
// loop over
btns.forEach(function (btn) {
// bind the click
btn.addEventListener('click', function (evt) {
// stop button click
evt.preventDefault()
// find the active one
var active = document.querySelector('.accordion.active')
// see if active button is the clicked button
var isSame = active == btn
// if we have an active button, remove the class
if (active) active.classList.remove('active')
// if the current button was not the active one add the class
if (!isSame) btn.classList.add('active')
})
})
button {
display: block;
}
button + div {
max-height: 0;
transition: max-height 0.25s ease-out;
overflow: hidden;
}
button.active {
background-color: green;
}
button.active + div {
max-height: 500px;
transition: max-height 0.5s ease-in;
}
<button class="accordion">The Ball 1</button>
<div><p>Ball 1</p><p>bounce bounce poounce</p></div>
<button class="accordion">The Ball 2</button>
<div><p>Ball 2</p><p> bounce pop</p></div>
<button class="accordion">The Ball 3</button>
<div><p>Ball 3</p><p>bounce bounce over the fence</p></div>
An alternative using JQuery.
I have changed the CSS to remove max-width and opacity and have set .panel elements to be display: none; initially:
div.panel {
position: absolute;
display: none;
}
And using JQuery, whenever we click an .accordion element, we hide the .panel elements (in case any others are showing), and then show the one that relates to the specifically clicked .accordion element.
// register clicks on accordion elements
$('.accordion').click(function() {
// hide each .panel related to every .accordion element
$('.accordion').next().hide();
// show the .panel next to our clicked .accordion element
$(this).next().show();
})

Categories