Close menu when link is clicked - javascript

I have an off-canvas menu that flies out when a toggle-button is clicked - so far so good. All menu links share the same class name (in this case .nav-link). I need the menu to close when any of the links are clicked, I think have selected them all and I think I have to loop through an array of the selection but I'm unsure implement it. Right now nothing happens when a link is clicked.
My HTML:
<ul class="nav-list">
<li class="nav-item">articles</li>
<li class="nav-item">tags</li>
<li class="nav-item">links</li>
<li class="nav-item">archive</li>
</ul>
My CSS:
.nav-list {
margin: 0;
margin-top: 3.2em;
padding: 0;
background: #777;
width: 100%;
transform: translateX(-100%);
transition: transform 300ms cubic-bezier(.5, 0, .5, 1);
}
My JS:
const navToggle = document.querySelector('.nav-toggle')
const navLink = document.querySelectorAll('.nav-link')
navToggle.addEventListener('click', () => {
document.body.classList.toggle('nav-open')
})
navLink.addEventListener('click', () => {
document.body.classList.remove('nav-open')
})

You're trying to add an event listener to a collection of nodes (querySelectorAll
for .nav-link vs querySelector for .nav-toggle). You can either iterate over the collection and add your click event listener to each item or simply listen to the parent element of the .nav-links:
const navToggle = document.querySelector('.nav-toggle')
// replace this with something more sensible
const navLinkParent = document.querySelector('.nav-link').parentElement;
navToggle.addEventListener('click', () => {
document.body.classList.toggle('nav-open')
})
// this is adding a click listener to ONE element
navLinkParent.addEventListener('click', (event) => {
// check if the clicked element matches what you're after
if (event.target.classList.contains('nav-link')) {
document.body.classList.remove('nav-open')
}
})

You must use a loop because querySelectorAll returns an array.
const navToggle = document.querySelector('.nav-toggle')
const navLink = document.querySelectorAll('.nav-link')
navToggle.addEventListener('click', () => {
document.body.classList.toggle('nav-open')
})
for (var i = 0; i < navLink.length; ++i) {
navLink[i].addEventListener('click', () => {
if(navToggle.classList.contains('nav-toggle')){
(navToggle.classList.remove('nav-toggle');
}
});
}

Related

Scroll function to navigate to appropriate section using JavaScript

My goal is to complete a dynamic single landing page using JavaScript. HTML and CSS files were already provided and I managed to build an unordered list by manipulating the DOM.
The thing that got me stuck is: When clicking an item from the navigation menu, the link should scroll to the appropriate section.
I cannot get this to work :/
Below is the JS code so far.
/* Declare variables for the fictive document and menu list to retrieve and store variables as unordered list */
const container = document.createDocumentFragment();
const menuList = document.getElementsByTagName('section');
/* Function to create the navigation menu as a clickable link */
function navigationLink(id, name) {
const navLink = `<a class = "menu__link" data-id=${id}">${name}</a>`;
return navLink;
}
/* Function for the navigation list, built as an unordered list */
function createNavigation() {
for (let i = 0; i < menuList.length; i++) {
const newMenuListItem = document.createElement('li');
const menuListName = menuList[i].getAttribute('data-nav')
const menuListID = menuList[i].getAttribute('id')
newMenuListItem.innerHTML = navigationLink(menuListID, menuListName)
container.appendChild(newMenuListItem);
}
/* Retrieve the id from the ul section to be added to the document fragment: container */
const navBarMenu = document.getElementById('navbar__list')
navBarMenu.appendChild(container);
}
// Add class 'active' to section when near the top of viewport
function setActiveClass() {
for (let i = 0; i < menuList.length; i++) {
if (isInViewport(menuList[i])) {
menuList[i].classList.add("your-active-class");
} else {
menuList[i].classList.remove("your-active-class");
}
}
}
To solve the problem I looked into another piece of code that I haven't been able to function properly.
function scrollToElement(event) {
if (event.target.nodeName === 'A') {
const menuListID = event.target.getAttribute('data-id');
const menu = document.getElementById(menuListID);
menu.scrollIntoView({ behavior: "smooth" });
}
}
document.addEventListener('scroll', function () {
setActiveClass();
});
const navBarMenu = document.getElementById('navbar__list')
navBarMenu.addEventListener('click', function (event) {
scrollToElement(event)
})
You can use anchor to do this. This will make your life easier than trying to do this in js.
To use it, you just have to set an id to a node. Like <div id="myFirstId"></div> and then, set the href of your link to #myFirstId.
If you want your scroll to not be instant, you can add the scroll-behavior: smooth to the scrollable element.
html {
scroll-behavior: smooth;
}
#scrollable {
overflow:auto;
}
#firstDiv {
background-color: green;
height: 700px
}
#secondDiv {
background-color: yellow;
height: 200px;
}
#thirdDiv {
background-color: blue;
height: 500px;
}
firstDiv
SecondDiv
thirdDiv
<div id="scrollable">
<div id="firstDiv"></div>
<div id="secondDiv"></div>
<div id="thirdDiv"></div>
</div>
For your code, just change this line <a class = "menu__link" data-id=${id}">${name}</a>
To this <a class="menu__link" href="#${id}">${name}</a>

Font color won't revert back to orginal color

The event only carries out one way and for some reason not the other way when the variable is clicked again.
I have multiple other events carrying out on the same variable and they all work perfectly fine. However, when it comes to this it doesn't.
Any suggestions?
//Font color change
for (let i = 0; i < li.length; i++) {
li[i].addEventListener('click', (e) => {
if (li[i].style.color == '#4D5067') {
li[i].style.color = '#C8CBE7'
} else {
li[i].style.color = '#4D5067'
}
})
}
Set the colours in CSS, and then use classList to toggle between the classes.
const ul = document.querySelector('ul');
ul.addEventListener('click', handleClick, false);
function handleClick({ target }) {
target.classList.toggle('light');
}
li { color: #4D5067; }
li.light { color: #C8CBE7; }
<ul>
<li>Test1</li>
<li>Test2</li>
<li>Test3</li>
<li>Test4</li>
</ul>

How to disable a div element using javascript?

hope everyone is keeping safe and coding a lot.
I am having a problem and have tried every solution but none are working. I do have a HAMBURGER MENU made of DIV's Elements, when open this menu will display all the MENU OPTIONS. So far it is working great but once the SECOND MENU is open I want to prevent the user to use the HAMBURGER MENU until the second menu is closed.
In my last try I have used POINTER-EVENTS: nome but believe it or not, it does stop the second menu from working and not the first one as it was supposed.
I will post here the code in JS and will be so grateful for any tips that will help me:
document.querySelector('.hamburger-menu').addEventListener('click', () => {
document.querySelector('.nav-wrapper').classList.toggle('change');
document.querySelector('#home-menu').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "block";
document.getElementById("#hamburger-menu").style['pointer-events'] = 'none';
});
document.querySelector('#close-window').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "none";
document.getElementById('.hamburger-menu').style.pointerEvents = "auto";
});
});
It seems the issue is because you are adding other event listener inside the top one. You may need to separate them
document.querySelector('.hamburger-menu').addEventListener('click', () => {
document.querySelector('.nav-wrapper').classList.toggle('change');
});
document.querySelector('#close-window').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "none";
document.getElementById('.hamburger-menu').style.pointerEvents = "auto";
});
document.querySelector('#home-menu').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "block";
document.getElementById(".hamburger-menu").style.pointerEvents = 'none';
});
I think you should leave the practice to control elements' behavior based on information gained from the DOM.
It would be much easier to control the behavior of your menu if you just toggled a boolean value in JS:
const btnFirst = document.getElementById('first')
const ddMenu = document.getElementById('second')
const ddItems = document.querySelectorAll('.dd-item')
const textArea = document.getElementById('third')
// this variable controls the behavior of the main menu
let btnFirstIsEnabled = true
btnFirst.addEventListener('click', function(e) {
if (btnFirstIsEnabled) {
btnFirstIsEnabled = false
ddMenu.classList.remove('hidden')
btnFirst.classList.add('disabled')
} else {
textArea.innerHTML = 'Cannot open dropdown again!'
}
})
ddItems.forEach(e => {
e.addEventListener('click', function(e) {
textArea.innerHTML = `Clicked: ${e.target.getAttribute('data-val')}`
ddMenu.classList.add('hidden')
btnFirstIsEnabled = true
btnFirst.classList.remove('disabled')
})
})
.main {
cursor: pointer;
}
.main.disabled {
color: #eaeaea;
}
.hidden {
display: none;
}
.dd-item {
cursor: pointer;
}
<div id="third">Clicked:</div>
<div id="first" class="main">OPEN</div>
<div id="second" class="hidden">
<ul>
<li class="dd-item" data-val="dd 1">Click dropdown item 1</li>
<li class="dd-item" data-val="dd 2">Click dropdown item 2</li>
<li class="dd-item" data-val="dd 3">Click dropdown item 3</li>
</ul>
</div>
I just added some coloring to the main menu in the snippet to show that it's not active, but the behavior is controlled by setting a Boolean variable in JS.
I think it would make your code much simpler if you made it work like this.

How to remove a class on all the other elements except on the element that was clicked

Hello I want to remove a class on all the other elements except on the clicked element.
my code is looking like this so far but i can not find a solution
const elements = document.querySelectorAll(".element");
function toggleOpen() {
this.classList.toggle('open');
elemets.forEach(ele => {
ele.classList.remove('open'); // buts this removes the class from all
the elements I am looking for something that removes the class on just
the other one element containing the class except on the clicked one.
})
}
elements.forEach(ele => ele.addEventListener('click', toggleOpen));
You were really close, just use an if clause in your forEach loop to skip the element you clicked by comparing with this, also, you had a typo in the function:
const elements = document.querySelectorAll('.element');
function toggleOpen() {
this.classList.toggle('open');
elements.forEach(ele => {
if (ele !== this) ele.classList.remove('open');
});
}
elements.forEach(ele => ele.addEventListener('click', toggleOpen));
.element { color: red; }
.open { color: green; }
<button class="element">1</button>
<button class="element">2</button>
<button class="element">3</button>
<button class="element">4</button>

How do I "toggle all", and also toggle specific classes of elements?

I want to toggle the visibility of classes of list items and also toggle the visibility of all the list items. With help of another stack overflow post, I am able to toggle specific classes of list items.
Here's the Javascript I have that toggles specific classes of list items:
var switcher = [false, false, false];
var classes = ['easy', 'fun', 'silly'];
$('.toggler').click(function () {
var x = $(this).hasClass('checked');
switcher[$(this).data('switch')] = !x;
$(this).toggleClass("checked", !x);
$('li').each(function () {
var cur = $(this);
cur.addClass('hidden');
$.each(switcher, function (index, data) {
if (data && cur.hasClass(classes[index])) {
cur.removeClass('hidden');
}
});
});
});
I added the basic functionality to hide and show all the list items, but the function brakes the individual class toggles:
$('.select_all').click(function () {
$(".toggler").toggleClass("checked");
$('li').toggleClass("hidden");
});
How can I keep class toggles, and add another button that toggles all the items?
Here's a fiddle example: http://jsfiddle.net/BMT4x/1/
I'm not sure if this is exactly what you're trying to do, but it should give you a push in the right direction (at least the direction I'd go). I prefer toggling everything with classes. Something like:
<button class="toggler" data-class="easy">Easy</button>,
<button class="toggler" data-class="fun">Fun</button>,
<button class="toggler" data-class="silly">Silly</button>,
<button class="toggler" data-class="all">Select All</button>
<ul id="list">
<li class="easy">Bowling</li>
<li class="fun">Balooning</li>
<li class="fun easy">Boating</li>
<li class="silly">Barfing</li>
<li class="easy fun">Bafooning</li>
</ul>
The CSS:
#list li {
display: none;
}
#list.easy li.easy {
display: block;
}
#list.fun li.fun {
display: block;
}
#list.silly li.silly {
display: block;
}
#list.all li {
display: block;
}
The JS:
$('.toggler').click(function () {
var category = $(this).data('class');
$('#list').toggleClass(category);
});
And a fiddle for reference: http://jsfiddle.net/qLLYj/
You can explicitly add/remove a class by passing a second, switch, parameter to toggleClass (see here).
So, you can change the state of all the individual switches and list items when clicking the .select_all button.
$('.select_all').click(function () {
$('.select_all').toggleClass("checked");
var allChecked = $('.select_all').hasClass("checked");
switcher = [allChecked, allChecked, allChecked];
$(".toggler").toggleClass("checked", allChecked);
$('li').toggleClass("hidden", !allChecked);
});
Some further changes made to get more intuitive behaviour
(e.g. if all checked, clicking one of the toggles deselects .select_all as well as itself; checking all individual toggles means .select_all is automatically checked):
$('.toggler').click(function () {
var x = $(this).hasClass('checked');
switcher[$(this).data('switch')] = !x;
$(this).toggleClass("checked");
$('li').each(function () {
var cur = $(this);
cur.addClass('hidden');
$.each(switcher, function (index, data) {
if (data && cur.hasClass(classes[index])) {
cur.removeClass('hidden');
}
});
});
var allChecked = switcher.indexOf(false) < 0;
$('.select_all').toggleClass("checked", allChecked);
});
Fiddle: http://jsfiddle.net/ET33B/

Categories