hope you all doing great.
I've been trying to add the (active class )to each of the navbar links when the user is on that specific section of the page with this
Tutorial (i'm stuck at 2:45:05) and no success so far can anyone tell me what i did wrong .thank you.
const menu = document.querySelector(' nav .container ul');
const navItems = menu.querySelectorAll('li');
navItems.forEach(item => {
const link = item.querySelector('a');
ink.addEventListener('click', () => {
link.classList.add(".active");
});
});
nav .container ul li a.active {
background: var(--color-primary);
color: var(--color-white);
}
<nav>
<div class="container">
<a href="#">
<h3> AMANI DEV </h3>
</a>
<ul>
<li>Home</li>
<li>About</li>
<li>Skills</li>
<li>Services </li>
<li>Portfolio </li>
<li>Contact Me </li>
</ul>
</div>
</nav>
Typo with ink not link.
When you assign a class with classList you don't include the .: classList.add('active').
In your CSS background should probably be background-color.
If you want to remove the other active links before applying the new one you can use forEach to iterate over the links and use classList.remove('active') on each one.
You may find event delegation easier to manage. Rather than attaching multiple listeners to multiple elements attach one listener to the list element that watches out for events from its child elements as they "bubble up the DOM. You can then check that the clicked element is a link, remove the active classes from the previous link(s), and then apply the active class to the clicked link.
Here's an example using event delegation.
// Cache the list, and the items
const list = document.querySelector(' nav .container ul');
const links = list.querySelectorAll('a');
// Add one listener to the list element
list.addEventListener('click', handleClick);
// If the clicked element is a link remove all
// the active classes from the other links, and then
// add the active class to the link that was clicked on
function handleClick(e) {
if (e.target.matches('a')) {
links.forEach(link => link.classList.remove('active'));
e.target.classList.add('active');
}
}
:root { --color-white: white; --color-primary: red; }
.active {
background-color: var(--color-primary);
color: var(--color-white);
}
<nav>
<div class="container">
<a href="#">
<h3> AMANI DEV </h3>
</a>
<ul>
<li><a class="active" href="#home">Home</a></li>
<li>About</li>
<li>Skills</li>
<li>Services </li>
<li>Portfolio </li>
<li>Contact Me </li>
</ul>
</div>
</nav>
You need to do querySelectorAll in a tag not on the li tag. Just do this and do let me know.
Modify the code in the following line :
ink.addEventListener('click',() => {
to
link.addEventListener('click',() => {
to be like this
const menu = document.querySelector(' nav .container ul');
const navItems = menu.querySelectorAll('li');
navItems.forEach(item => {
const link = item.querySelector('a');
link.addEventListener('click',() => {
link.classList.add(".active");
});
});
document.querySelectorAll('ul li').forEach(el => {
el.onclick = () => {
document.querySelectorAll('ul li').forEach(el => el.classList.remove('active'));
el.classList.add('active');
}
})
here a demo code:
document.querySelectorAll('#myNav li').forEach(el => {
el.onclick = () => {
document.querySelectorAll('#myNav li').forEach(el => el.classList.remove('active'));
el.classList.add('active');
}
})
.active {
font-size: 70px;
}
<nav>
<div class="container">
<a href="#">
<h3> AMANI DEV </h3>
</a>
<ul id="myNav">
<li><a class="active" href="#home">Home</a></li>
<li>About</li>
<li>Skills</li>
<li>Services </li>
<li>Portfolio </li>
<li>Contact Me </li>
</ul>
</div>
</nav>
// selecting all a element on the page
const links = document.querySelectorAll('a');
// adding a click event on all elements
links.forEach((link) => {
link.addEventListener('click', (e) => {
// if we click first thing is deleting the active class from all link
links.forEach((link) => {
link.classList.remove('active')
})
// then in the end add the active class only in the correct one
e.target.classList.add('active')
})
})
Related
I have created code for an open/close toggle filter by targeting the first li of the filter. For some reason, the children li's that are nested inside this are also closing the toggle when selected to filter the feed.
Please would someone be able to advise how to only target the parent li without the children li affecting the toggle from opening and closing?
Here is a link to the page with the filter. Please test the toggle and select a filter to see the issue that I am currently facing.
https://snapstaging.co.uk/coolkitnew/vans/
It may be worth noting that I don't want to target by class because I have multiple of these elements all with different classes.
let filterBlock = document.querySelectorAll('.searchandfilter li ul')
let filterLi = document.querySelectorAll('.searchandfilter ul li')
let filterLiOpen = [];
filterLi.forEach((tag, index) => {
tag.addEventListener('click', () => {
filterLi[index].classList.toggle('active')
if (!filterLiOpen[index]) {
filterLiOpen[index] = true;
} else if (filterLiOpen[index]) {
filterLiOpen[index] = false;
}
console.log(filterLiOpen);
})
})
.active { color: #C00; }
<div class="searchandfilter">
<ul>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0"><label class="sf-label-checkbox">Large Van<span class="sf-count">(20)</span></label></li>
</ul>
</li>
</ul>
</div>
The issue is because you select all the li elements in the DOM. To target only those which are children of the top level ul, not its descendants, use the child operator in your selector: >.
Also note that you can simplify the logic which toggles the boolean value you store in your array. Here's a working example:
let filterLi = document.querySelectorAll('.searchandfilter > ul > li')
let filterLiOpen = [];
filterLi.forEach((tag, index) => {
tag.addEventListener('click', e => {
e.currentTarget.classList.toggle('active');
filterLiOpen[index] = !filterLiOpen[index];
console.log(filterLiOpen);
})
})
.active { color: #C00; }
<div class="searchandfilter">
<ul>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0">
<label class="sf-label-checkbox">
Large Van
<span class="sf-count">(20)</span>
</label>
</li>
</ul>
</li>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0">
<label class="sf-label-checkbox">
Medium Van
<span class="sf-count">(10)</span>
</label>
</li>
</ul>
</li>
<li class="sf-field-post-meta-vehicle_size">
<h4>Size</h4>
<ul data-operator="and" class="">
<li class="sf-level-0">
<label class="sf-label-checkbox">
Small Van
<span class="sf-count">(5)</span>
</label>
</li>
</ul>
</li>
</ul>
</div>
I have a navigation bar.
To start with only the first link (Homepage) has a class of 'active'.
I want to achieve the following:
when the user click on a different link the class of active is removed from wherever is present and is attributed to the link the user has clicked.
So that only one link at a time is active.
const links = document.querySelectorAll('.navbar ul li a');
links.forEach((link) => {
link.addEventListener('click', () => {
link.classList.toggle('active');
});
});
.active {
background-color: #fc9d03;
}
<div class="navbar">
<ul>
<li>Homepage</li>
<li> Blog Entries</li>
<li>Food Gallery</li>
<li> Contact Us</li>
</ul>
</div>
I edited your snippet a little.
On event listener function you can get all links to be deactivated by filtering out the one clicked from all links.
Then you can remove activate class from these links and add it to clicked one.
const links = document.querySelectorAll('.navbar ul li a');
links.forEach((link) => {
link.addEventListener('click', () => {
const notClickedLinks = Array.from(links).filter((notClickedLink) => {
return notClickedLink !== link;
});
notClickedLinks.forEach((notClickedLink) => {
notClickedLink.classList.remove('active');
});
link.classList.add('active');
});
});
.active {
background-color: #fc9d03;
}
<div class="navbar">
<ul>
<li>Homepage</li>
<li>Blog Entries</li>
<li>Food Gallery</li>
<li>Contact Us</li>
</ul>
</div>
My answer has been provided by #Chris G here in the comments:
Instead of just toggling it, remove it from all others, then add it back to the current one: jsfiddle.net/rcfx75m9 – Chris G 18 hours ago
How can i add a class to an <li> element which has a child <ul> element. I found there are so many examples in jQuery but not in Javascript. I would like to do it in Pure Javascript for Performance Optimization. The code is as follows: Thanks in Advance!
<nav id="navigation">
<li>Home</li>
<li> <!-- >>> I want to add a class to this <li> element as it has a <ul> child element -->
Services
<ul>
<li>Graphic Designing</li>
<li>Web Designing</li>
</ul>
</li>
<li>Contact</li>
</nav>
I think you need something like this
// -------- vanilla js
document.querySelectorAll('li').forEach((el) => {
if(el.querySelector('ul')) el.classList.add('theClassNameYouNeed');
});
// -------- jQuery (just to see the difference) :)
$('li').each(function() {
const el = $(this);
if(el.find('ul').length) el.addClass('theClassNameYouNeed');
});
Useful resources to move from jQuery to pure Javascript (Vanilla):
http://youmightnotneedjquery.com/
https://tobiasahlin.com/blog/move-from-jquery-to-vanilla-javascript/
https://gist.github.com/joyrexus/7307312
document.querySelectorAll('li').forEach((el) => {
if(el.querySelector('ul')) el.classList.add('theClassNameYouNeed');
});
.theClassNameYouNeed {
background: green;
}
<li>Home</li>
<li>
Services
<ul>
<li>Graphic Designing</li>
<li>Web Designing</li>
</ul>
</li>
<li>Contact</li>
This is one possible way of adding the class to the li element which has ul as child
const liElem = document.querySelectorAll("li")
liElem.forEach(elem => {
if(elem.querySelector("ul")) {
elem.classList.add("new-class");
}
})
.new-class {
color: red;
background: #ececec
}
<li>Home</li>
<li>
Services
<ul>
<li>Graphic Designing</li>
<li>Web Designing</li>
</ul>
</li>
<li>Contact</li>
Demo snippet
const liElems = document.querySelectorAll('li');
liElems.forEach((elem) => {
const childrenElems = elem.querySelectorAll('ul')
if(childrenElems.length > 0){
elem.setAttribute("class", "democlass")
}
});
<html>
<head></head>
<body>
<li>Home</li>
<li>
Services
<ul>
<li>Graphic Designing</li>
<li>Web Designing</li>
</ul>
</li>
<li>Contact</li>
<script src="script.js"></script>
<body>
</html>
You can do this two ways below using only JavaScript off-course.
Here is simple direct solution to add class to the li which has ul in it. This selects the first li > ul in the DOM and apply class to it using classList and add method.
Live Demo:
window.addEventListener('DOMContentLoaded', (event) => {
let getLiUL = document.querySelector('li > ul')
getLiUL.parentElement.classList.add('foo')
});
.foo {
background: green;
}
<nav>
<li>Home</li>
<li>I want to add a class to this element as it has a child element
Services
<ul>
<li>Graphic Designing</li>
<li>Web Designing</li>
</ul>
</li>
<li>Contact</li>
</nav>
Here is solution which uses querySelectorAll method and forEach loop. This loops through all the li in the DOM and apply class to the only li which has ul as a child.
Live Demo:
window.addEventListener('DOMContentLoaded', (event) => {
let getLiUL = document.querySelectorAll('li')
getLiUL.forEach(function(e) {
e.querySelector('ul') ? e.classList.add('foo') : " "
})
});
.foo {
background: green;
}
<nav>
<li>Home</li>
<li>I want to add a class to this element as it has a child element
Services
<ul>
<li>Graphic Designing</li>
<li>Web Designing</li>
</ul>
</li>
<li>Contact</li>
</nav>
References:
ClassList
querySelector
querySelectorAll
This question already has answers here:
How to addEventListener to multiple elements in a single line
(15 answers)
Closed 2 years ago.
I know this question has been asked before, but this one has a bit of a twist.
I've seen a few downvotes on this questions. Please be so kind as to let me know your reasons for downvoting so that I will be able to correct this in future. Thanks for your input all.
I have a single page scroll website with a nav menu overlay on screens smaller than 992px. The menu toggles fine, however when a nav link is clicked the nav menu remains open with the exception of the first nav-link.
I like to have every link closing the nav menu on click.
So How do get all the nav links to close the nav menu on click? I have a hunch it has to do with using querySelectorAll instead of just querySelector.
Here's a link to the site: https://portfolioprime.github.io/robotics/
Any assistance would be greatly appreciated.
Here's the navigation html.
HTML
<body>
<header>
<nav class="nav">
<!-- Nav Menu -->
<ul class="nav-list">
<li class="nav-item">
Home</li>
<li class="nav-item">
Robots</li>
<li class="nav-item">
Projects</li>
<li class="nav-item">
Research</li>
<li class="nav-item">
Explore</li>
<li class="nav-item">
Prosthetics</li>
<li class="nav-item">
Contact</li>
</ul>
<!-- Menu-toggle -->
<div class="menu-toggle">
<i class="fas fa-bars"></i>
<i class="fas fa-times"></i>
</div>
</nav>
</header>
</body>
And here's the Javascript.
JS
// Select element function
const selectElement = function (element) {
return document.querySelector(element);
};
let menuToggler = selectElement('.menu-toggle');
let body = selectElement('body');
let menuClose = selectElement('.nav-link');
// Open/Close menu on .menu-toggle click
menuToggler.addEventListener('click', function () {
body.classList.toggle('open');
});
// Close menu on .nav-link click
menuClose.addEventListener('click', function () {
body.classList.remove('open');
});
And you may be interested in the CSS for the .open class that is appended to the body with javascript.
CSS
.open .nav-list {
bottom: 0;
}
.nav-link:hover {
border-bottom: none;
}
.menu-toggle {
display: block;
}
.open .menu-toggle .fa-bars {
display: none;
}
.open .menu-toggle .fa-times {
display: block;
position: fixed;
right: 2.7rem;
top: 2rem;
}
Your hunch is totally correct. This does it.
// Select element function
const selectElement = (element) =>
document.querySelector(element);
const getAllWithClass = (className) =>
document.getElementsByClassName(className);
const
body = selectElement('body'),
// Converts the returned collection to a proper Array
navLinks = Array.from(getAllWithClass("nav-link"));
// Close menu on .nav-link click
navLinks.forEach(link => { // The Array method `forEach` loops through
link.addEventListener('click', function() {
body.classList.remove('open');
console.log("(No more blue background means it's closed)");
});
});
.open .nav-list {
background-color: lightskyblue;
}
<body class="open">
<ul class="nav-list">
<li class="nav-item">
Home</li>
<li class="nav-item">
Robots</li>
<li class="nav-item">
Projects</li>
</ul>
</body>
Note: I think it would be better to add a single click-listener on the whole menu, (and check that the target of any click event is a nav-link before proceeding). But since you wanted to see how to add multiple listeners at once, I stuck with this.
I am making a dropdown menu in a dropdown, I want that I have a few head items and if you click on one, the dropdown in that head item is displayed as a block element. But the problem is that they all have the same class and when I want to add a class all the dropdowns inside the head items get that class. What am I doing wrong here?
jQuery(document).ready(function(){
var click = false;
jQuery(".navbar-collapse .nav li").click(function() {
if(click == false) {
jQuery(".navbar-collapse .nav li ul").addClass('clicked');
click = true;
} else {
jQuery(".navbar-collapse .nav li ul").removeClass('clicked');
click = false;
}
});
});
.clicked {
display: block !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="nav menu">
<li class="item-101 default deeper parent">
Home
<ul class="nav-child unstyled small clicked">
<li class="item-124">
Maandmail
</li>
</ul>
</li>
<li class="item-102 default deeper parent">
Contact
<ul class="nav-child unstyled small clicked">
<li class="item-125">
Contact pagina
</li>
</ul>
</li>
</ul>
Does anyone know why this is happening and how to fix this issue?
You have to make the code look at the specific UL relative to the item you've clicked on:
jQuery(document).ready(function() {
var click = false;
jQuery(".navbar-collapse .nav li").click(function(e) {
if (click == false) {
jQuery(e.currentTarget).find("ul").addClass('clicked');
click = true;
} else {
jQuery(e.currentTarget).find("ul").removeClass('clicked');
click = false;
}
});
});