How to display only one item at a time using JavaScript - javascript

I'm creating a sidebar menu for Archiving news items. My list shows the years of the news items: 2019, 2018, 2017. When clicked these then display a drop-down container showing the months - September 2019, August 2019, etc.
I'm using a bit of JavaScript to display the drop-down container. The problem I have is that when you click on 2019 and then 2018 both years are showing their relevant months. How do I only show the information for one year at a time? i.e. when clicking on 2019 it displays the months for that year, then when I click on 2018 the content for 2019 hides and only the content for 2018 then shows.
var dropdown = document.getElementsByClassName("dropdown-btn-2");
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", function() {
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
});
}
.dropdown-container-2 {
display: none;
background-color: transparent;
}
.dropdown-btn-2 {
display: inline-block;
}
<div class="py-1">
<div class="dropdown-btn-2">
<i class="fas fa-chevron-circle-right fa-fw colour-2"></i> 2019
</div>
<div class="dropdown-container-2 ml-4">
<ul class="list-unstyled">
<li class="pt-1">December 2019</li>
<li class="pt-1">November 2019</li>
<li class="pt-1">October 2019</li>
<li class="pt-1">September 2019</li>
</ul>
</div>
</div>
<div class="py-1">
<div class="dropdown-btn-2">
<i class="fas fa-chevron-circle-right fa-fw colour-2"></i> 2018
</div>
<div class="dropdown-container-2 ml-4">
<ul class="list-unstyled">
<li class="pt-1">December 2018</li>
<li class="pt-1">November 2018</li>
<li class="pt-1">October 2018</li>
<li class="pt-1">September 2018</li>
</ul>
</div>
</div>

Whenever you toggle a dropdown, remove the active class from all the other ones.
Also it's better to add the active class to the dropdown container and then hide the content when that class is missing using css.
Finally, I think it's more neat to manage each group separately (loop the dropdown containers and then add the event listener to the button that is inside them).
document.querySelectorAll(".py-1").forEach(function(dropdown, index, list) {
dropdown.querySelector(".dropdown-btn-2").addEventListener("click", function() {
list.forEach(function(item) {
if (item !== dropdown) item.classList.remove("active");
});
dropdown.classList.toggle("active");
});
});
.dropdown-btn-2 {
display: inline-block;
}
.py-1>.dropdown-container-2 {
background-color: transparent;
display: none;
}
.py-1.active>.dropdown-container-2 {
display: block;
}
<div class="py-1">
<div class="dropdown-btn-2">
<i class="fas fa-chevron-circle-right fa-fw colour-2"></i> 2019
</div>
<div class="dropdown-container-2 ml-4">
<ul class="list-unstyled">
<li class="pt-1">December 2019</li>
<li class="pt-1">November 2019</li>
<li class="pt-1">October 2019</li>
<li class="pt-1">September 2019</li>
</ul>
</div>
</div>
<div class="py-1">
<div class="dropdown-btn-2">
<i class="fas fa-chevron-circle-right fa-fw colour-2"></i> 2018
</div>
<div class="dropdown-container-2 ml-4">
<ul class="list-unstyled">
<li class="pt-1">December 2018</li>
<li class="pt-1">November 2018</li>
<li class="pt-1">October 2018</li>
<li class="pt-1">September 2018</li>
</ul>
</div>
</div>
Keep in mind that if cannot have smooth transitions when using display: none and display: block to hide and show the element (opacity:0 and opacity:1 works)

When the click event is fired, you should look for all the other active dropdowns and remove the class. It will become something like this:
var dropdown = document.getElementsByClassName("dropdown-btn-2");
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", function() {
// Start changes
var activeDropdowns = document.querySelectorAll(".dropdown-btn-2.active");
for (btn of activeDropdowns) {
if (btn != this) {
btn.classList.remove("active");
var cont = btn.nextElementSibling;
cont.style.display = "none";
}
}
// End changes
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
});
}
.dropdown-container-2 {
display: none;
background-color: transparent;
}
.dropdown-btn-2 {
display: inline-block;
}
<div class="py-1">
<div class="dropdown-btn-2">
<i class="fas fa-chevron-circle-right fa-fw colour-2"></i> 2019
</div>
<div class="dropdown-container-2 ml-4">
<ul class="list-unstyled">
<li class="pt-1">December 2019</li>
<li class="pt-1">November 2019</li>
<li class="pt-1">October 2019</li>
<li class="pt-1">September 2019</li>
</ul>
</div>
</div>
<div class="py-1">
<div class="dropdown-btn-2">
<i class="fas fa-chevron-circle-right fa-fw colour-2"></i> 2018
</div>
<div class="dropdown-container-2 ml-4">
<ul class="list-unstyled">
<li class="pt-1">December 2018</li>
<li class="pt-1">November 2018</li>
<li class="pt-1">October 2018</li>
<li class="pt-1">September 2018</li>
</ul>
</div>
</div>
Since only one element will be shown at a time, you can also use "querySelector" instead of "querySelectorAll" and avoid the loop, but this is probably safer.

How about updating ONLY your javascript like this:
var dropdown = document.getElementsByClassName("dropdown-btn-2");
var i;
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", function() {
var container = document.getElementsByClassName("dropdown-container-2");
var dropdownContent = this.nextElementSibling;
if(dropdownContent.style.display == "block") {
dropdownContent.style.display = "none";
return;
}
for (var j = 0; j < container.length; j++) {
if(container[j].style.display = "block") {
container[j].style.display = "none";
}
}
dropdownContent.style.display = "block";
});
}
Check all other containers and hide them
toggles show/hide if you are clicking on the same element
codePen available Here: https://codepen.io/ermiarch/pen/XWrQodo

Related

active anchor link not being highlighted

I have been staring at this code for far too long, unfortunately I do not see the problem.
I am trying to get the active menu entry highlighted when the relevant div gets scrolled into view. But nothing is happening and no errors are being thrown in the console.
My menu html:
<section class="LeftAnchorNav" style="display: block;">
<nav id="LeftAnchorNav">
<div class="container" style="padding-left: 50px;">
<div class="col-md-4 LeftAnchorNavWrapper">
<ul class="LeftAnchorNavMenu">
<li class="leftanchorlink">
<a class="leftlink" href="#20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9">About us</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#d736bc13-a2a7-48d4-8ecc-75b9a17f801b">Demo Center</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#545a6339-87e4-41ed-ad51-70c3788cedee">Testimonial</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#9355324a-6219-4300-ae97-aa77bf67dab4">Newsletter</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#0c70b0db-3e70-4faa-ab98-154b4eae498e">Blog</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#4903bc53-b862-42f0-a600-e21061204e42">Contact</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#002f6fd7-758b-4b27-8c75-0ce087ee826a">Solution Finder</a>
</li>
</ul>
</div>
</div>
</nav>
</section>
An example div:
<div class="block anchorblock col-lg-12 col-md-12 col-sm-12 col-xs-12 span12 "><div id="20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9"></div>
</div>
My jquery/js:
if ($('.LeftAnchorNav').length > 0) {
// prepare the variables
var lastID;
var anchorMenu = $(".LeftAnchorNavMenu");
var anchorMenuHeight = anchorMenu.outerHeight() + 100;
var anchorMenuItems = anchorMenu.find(".leftlink");
var anchorMenuItemsTarget = anchorMenuItems.map(function () {
var item = $($(this).attr("href"));
if (item.length) { return item; }
});
// bind everything to the scrolling
$(window).scroll(function () {
// get anchornav container scroll position and add buffer
var fromTop = $(this).scrollTop() + anchorMenuHeight + 300;
// get ID of the current scroll item
var currentItem = anchorMenuItemsTarget.map(function () {
if ($(this).offset().top < fromTop)
return this;
});
// get the ID of the current element
currentItem = currentItem[currentItem.length - 1];
var id = currentItem && currentItem.length ? currentItem[0].id : "";
if (lastID !== id) {
lastID = id;
// Set/remove active class
anchorMenuItems.removeClass("highlightleftnavactive")
anchorMenuItems.filter("[href='#" + id + "']").addClass("highlightleftnavactive");
}
});
}
It's quite fiddly to do the arithmetic for scrolling so this snippet uses IntersectionObserver instead. This has the added benefit of less processing overhead as it just gets informed when the elements come in or go out of view, not every time the user scrolls a bit.
It sets up the observer to observe when any of the relevant elements come into or go out of the viewport. When alerted to that it adds or removes the highlighting class to the related navbar link.
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
<style>
.LeftAnchorNav {
position: fixed;
z-index:1;
}
.tall {
width: 100vw;
height: 100vh;
background-image: linear-gradient(cyan, magenta, yellow, black);
}
.highlightleftnavactive {
background-color: yellow;
}
</style>
</head>
<section class="LeftAnchorNav" style="display: block;">
<nav id="LeftAnchorNav">
<div class="container" style="padding-left: 50px;">
<div class="col-md-4 LeftAnchorNavWrapper">
<ul class="LeftAnchorNavMenu">
<li class="leftanchorlink">
<a class="leftlink" href="#20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9">About us</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#d736bc13-a2a7-48d4-8ecc-75b9a17f801b">Demo Center</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#545a6339-87e4-41ed-ad51-70c3788cedee">Testimonial</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#9355324a-6219-4300-ae97-aa77bf67dab4">Newsletter</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#0c70b0db-3e70-4faa-ab98-154b4eae498e">Blog</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#4903bc53-b862-42f0-a600-e21061204e42">Contact</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#002f6fd7-758b-4b27-8c75-0ce087ee826a">Solution Finder</a>
</li>
</ul>
</div>
</div>
</nav>
</section>
<div class="tall"></div>
<div class="block anchorblock col-lg-12 col-md-12 col-sm-12 col-xs-12 span12 "><div id="20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9">
An example block coming into and going out of view it belongs to the About us link in the navbar</div>
</div>
<div class="tall"></div>
<script>
let callback = (entries) => {
entries.forEach(entry => {
let id = entry.target.firstChild.id;
let leftLink = document.querySelector("a.leftlink[href='#"+ id + "']");
if (entry.isIntersecting) { leftLink.classList.add('highlightleftnavactive');}
else { leftLink.classList.remove('highlightleftnavactive');}
});
};
const observer = new IntersectionObserver(callback);
const anchorBlocks = document.querySelectorAll('.anchorblock');
anchorBlocks.forEach( (anchorBlock) => {
observer.observe(anchorBlock);
});
</script>

Change active link color

Problem
I have an issue with my website, I want to change the color of the selected link to blue, and if other is selected put it back to gray, but don`t know how to target the clicked link in javascript. Here is my code.
var center = document.getElementsByClassName("center");
for (let i = 0; i < center.length; i++) {
center[i].addEventListener("click", DivSelector());
}
function DivSelector() {
for (let i = 0; i < center.length; i++) {
center[i].classList.Remove('active');
};
}
.nav-item {
text-decoration: none;
color: #505b67;
margin-left: 10px;
}
.active {
color: #4460f1;
}
<ul class="nav-tab-ul">
<li id="Profile">
<a class="center nav-item" href="">Profile</a>
</li>
<li id="Change-Password">
<a class="center nav-item" href="">Change password</a>
</li>
<li id="Notifications">
<a class="center nav-item" href="">Notifications</a>
</li>
<li id="My-Cards">
<a class="center nav-item" href="">My Cards</a>
</li>
</ul>
Don`t know how to select the clicked link here for adding the 'active' class.
Try this. its working
var center = document.getElementsByClassName("center");
for (let i = 0; i < center.length; i++) {
center[i].addEventListener("click", function(e){
for (let i = 0; i < center.length; i++) {
center[i].classList.remove('active');
e.target.classList.add('active');
}
})
}
All you need to do is add the class active to the actual link of the page you are on. So when you on the profile page you add active to profile link and none of the others
PROFILE PAGE
<ul class="nav-tab-ul">
<li id="Profile">
<a class="center nav-item active" href="">Profile</a>
</li>
<li id="Change-Password">
<a class="center nav-item" href="">Change password</a>
</li>
<li id="Notifications">
<a class="center nav-item" href="">Notifications</a>
</li>
<li id="My-Cards">
<a class="center nav-item" href="">My Cards</a>
</li>
</ul>
CHANGE PASSWORD PAGE
When you on the change password page you add the active class to that link and none of the others.
<ul class="nav-tab-ul">
<li id="Profile">
<a class="center nav-item" href="">Profile</a>
</li>
<li id="Change-Password">
<a class="center nav-item active" href="">Change password</a>
</li>
<li id="Notifications">
<a class="center nav-item" href="">Notifications</a>
</li>
<li id="My-Cards">
<a class="center nav-item" href="">My Cards</a>
</li>
</ul>
If these are separate pages there is no need for JS?
If you are using one header file and including the file into all your pages then it will be a different solution. Let us know why you want to use JS
You can get the element clicked in event.currentTarget.
const nodes = document.getElementsByClassName("center");
for (let i = 0; i < nodes.length; i++) {
nodes[i].addEventListener("click", divSelector);
}
function divSelector(event) {
removeAllActives();
event.currentTarget.className += ' active';
}
function removeAllActives() {
const actives = document.getElementsByClassName("active");
for (let i = 0; i < actives.length; i++) {
actives[i].classList.remove('active');
}
}
.active {
color: blue;
}
<a class="center"> Link 1 </a> <br/>
<a class="center"> Link 2 </a>
A few things:
When adding your event listener, you called DivSelector, meaning you added the return value of DivSelector() as a listener, which is void. Instead just pass the name of the function you want to add as a listener.
You need to add the 'active' class to the clicked element after removing it from all the others. You can get the clicked element by using the event argument passed to your event listener. The element will be event.target.
DivSelector should be defined before it's used, so it should be moved above the rest of the code.
Here's an example:
function DivSelector(event) {
for (let i = 0; i < center.length; i++) {
center[i].classList.remove('active');
}
event.target.classList.add('active');
}
let center = document.querySelectorAll("center");
for (let i = 0; i < center.length; i++) {
center[i].addEventListener("click", DivSelector);
}
.nav-item {
text-decoration: none;
color: #505b67;
margin-left: 10px;
}
.active {
color: #4460f1;
}
<ul class="nav-tab-ul">
<li id="Profile">
<a class="center nav-item" href="#">Profile</a>
</li>
<li id="Change-Password">
<a class="center nav-item" href="#">Change password</a>
</li>
<li id="Notifications">
<a class="center nav-item" href="#">Notifications</a>
</li>
<li id="My-Cards">
<a class="center nav-item" href="#">My Cards</a>
</li>
</ul>

How to set the nav ul display to none when clicked second time on the hamburger?

I am developing a responsive website in which when clicked on hamburger, displays a nav ul, but I have not been able to set the list display to none when clicked on the hamburger second time.
HTML
<div class=""container hamburger" onclick="toggleHamburger(this)" id="hamburger">
<div class="bar1"></div>
<div class="bar2"></div>
<div class="bar3"></div>
</div>
<ul class="col-m-9 navlist" id="navlist">
<li><a id="HTML" href="#">HTML</a></li>
<li><a id="CSS" href="#">CSS</a></li>
<li><a id="JS" href="#">JS</a></li>
<li><a id="FAQ" href="#">FAQ</a></li>
<li><a id="About" href="#">About</a></li>
JS
function toggleHamburger(hamburg) {
hamburg.classList.toggle("change");
displayNavList();
}
function displayNavList(){
var navList = document.getElementById("navlist");
navList.style.display = "block";
}
So, basically I want to know that if there is any way in JS to call another function when clicked on the hamburger second time.
See the problem on AdiCodes, with the hamburger, it can open the nav list but cannot close it.
Is that what you want to achieve ? or did I misunderstand the question ?
var hamburger = document.querySelector('#hamburger'),
navList = document.querySelector('#navlist');
var toggleNav = function() {
hamburger.classList.toggle('js-active')
navList.classList.toggle('js-active')
}
hamburger.addEventListener('click', toggleNav)
#hamburger {
color: red;
}
.js-active#hamburger {
color: green;
}
#navlist {
display: none;
}
.js-active#navlist {
display: block;
}
<div class="container hamburger" id="hamburger">
HAMBURGER
</div>
<ul class="col-m-9 navlist" id="navlist">
<li><a id="HTML" href="#">HTML</a></li>
<li><a id="CSS" href="#">CSS</a></li>
<li><a id="JS" href="#">JS</a></li>
<li><a id="FAQ" href="#">FAQ</a></li>
<li><a id="About" href="#">About</a></li>
</ul>
Why don't you show/hide your list with a class, then you can toggle that class in your click handler too:
function toggleHamburger(hamburg) {
hamburg.classList.toggle("change");
var navList = document.getElementById("navlist");
navList.classList.toggle("shown");
}
With the CSS:
#navlist:not(.shown) {
display: none;
}
You can define a global counter
var count = 0;
The first time you click it will show your nav, and the second time it will toggle.
var count = 0;
function toggleHamburger(hamburg) {
if(count >= 1){
var navList = document.getElementById("navlist");
navList.style.display = "none";
}else{
hamburg.classList.toggle("change");
displayNavList();
}
count ++;
}
function displayNavList(){
var navList = document.getElementById("navlist");
navList.style.display = "block";
}
<div class="container hamburger" onclick="toggleHamburger(this)" id="hamburger">
<div class="bar1">test</div>
<div class="bar2">test</div>
<div class="bar3">test</div>
</div>
<ul class="col-m-9 navlist" id="navlist" style="display: none;">
<li><a id="HTML" href="#">HTML</a></li>
<li><a id="CSS" href="#">CSS</a></li>
<li><a id="JS" href="#">JS</a></li>
<li><a id="FAQ" href="#">FAQ</a></li>
<li><a id="About" href="#">About</a></li>

How do I keep a submenu open when clicked?

This was created by someone else, but some submenus stay open while one stays closed when clicked. I have an idea of what I can do, but I have very little knowledge of JS. I was thinking if sub5 (the id for the submenu that stays closed), "something to make it true, then false if not sub5" How can I do this?
HTML
<nav id="rightNav">
<div class="menutitle first" onClick="SwitchMenu('sub1')">fabrics</div>
<div id="sub1" class="submenu">
<ul>
<li>lee jofa</li>
<li>groundworks</li>
<li>threads</li>
<li>gp & j baker</li>
<li>mulberry home</li>
<li>baker lifestyle</li>
<li>blithfield</li>
</ul>
</div>
<!-- end div#sub1 (fabrics) -->
<div class="menutitle" onClick="SwitchMenu('sub2')">furniture</div>
<div id="sub2" class="submenu">
<ul>
<li>lee jofa upholstery</li>
<li>lee jofa occasionals</li>
<li>Bunny Williams Home</li>
<li>holland & co.</li>
<li>macrae</li>
<li>elle & marks</li>
</ul>
</div>
<!-- end div#sub2 (furniture) -->
<div class="menutitle" onClick="SwitchMenu('sub3')">wallcoverings</div>
<div id="sub3" class="submenu">
<ul>
<li>lee jofa</li>
<li>cole & son</li>
<li>groundworks</li>
<li>threads</li>
<li>gP & j baker</li>
<li id="blithfield-wall">blithfield</li>
<li>farrow & ball</li>
<li>lincrusta</li>
</ul>
</div>
<!-- end div#sub3 (wallcoverings) -->
<div class="menutitle">trimmings</div>
<div class="menutitle" onClick="SwitchMenu('sub5')">carpets</div>
<div id="sub5" class="submenu">
<ul>
<li>lee jofa carpet</li>
<li id="gw-carpet">groundworks</li>
<li>threads</li>
</ul>
</div>
<!-- end div#sub5 (carpets) -->
<div class="menutitle" id="archive-collection">designer collections</div>
<div class="menutitle last">archives</div>
</nav>
<!-- end nav#rightNav -->
JS
if (document.getElementById) { //DynamicDrive.com change
document.write('<style type="text/css">\n')
document.write('.submenu{display: none;}\n')
document.write('</style>\n')
}
function SwitchMenu(obj) {
if (document.getElementById) {
var el = document.getElementById(obj);
var ar = document.getElementById("rightNav").getElementsByTagName("div"); //DynamicDrive.com change
if (el.style.display != "block") { //DynamicDrive.com change
for (var i = 0; i < ar.length; i++) {
if (ar[i].className == "submenu") //DynamicDrive.com change
ar[i].style.display = "none";
}
el.style.display = "block";
} else {
el.style.display = "none";
}
}
}
You have a class and a style.
Your style is defined in the class, so you can't access it with element.style.
A simple solution is to change this line:
<div id="sub5" class="submenu">
to
<div id="sub5" class="submenu" style="display:none;">

Load JavaScript if Breadrumb exists

So i made a javascript and would like to make it load only if a certain breadcrumb exists in a page, lets say this breadcrumb is called "support" and my breadcrumb code is:
<ol class="breadcrumb top ipsList_inline left" id="breadcrumb">
<li itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb" class="first">
<a href="https://www.website.com" itemprop="url">
<span itemprop="title">website</span>
</a>
</li>
<li itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">website</span>
</li>
<li itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">Section</span>
</li>
<li itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">Support</span>
</li>
</ol>
You can do something like this:
window.onload = function () {
var breadcrumb = document.getElementById("breadcrumb");
var titles = breadcrumb.getElementsByTagName("span");
var support = 0;
for(var i = 0; i < titles.length; ++i){
support += titles[i].innerHTML == "Support";
}
if(support) initialize();
}
function initialize() {
/* Do your stuff. */
}

Categories