Make vanilla JS accordion auto close previous section - javascript

I found this great accordion with a very compact code but what I miss here is a function to auto close previous section when opened another one. It's probably quite easy but I'm a complete JS noob, sadly.
Original code source.
const items = document.querySelectorAll(".accordion a");
function toggleAccordion(){
this.classList.toggle('active');
this.nextElementSibling.classList.toggle('active');
}
items.forEach(item => item.addEventListener('click', toggleAccordion));

What have You tried so far ?
If You didn't, try the following logic.
Before you give an element a activeclass - loop over the rest of the elements and remove it from all of them :)
const items = document.querySelectorAll(".accordion a");
const remove = () => {
items.forEach(el => {
el.classList.remove('active');
el.nextElementSibling.classList.remove('active');
})
}
function toggleAccordion(){
remove()
this.classList.toggle('active');
this.nextElementSibling.classList.toggle('active');
}
items.forEach(item => item.addEventListener('click', toggleAccordion));

you could store the active one...
const items = document.querySelectorAll(".accordion a");
let active = null;
function toggleAccordion(){
if(active){
active.classList.toggle('active');
}
this.classList.toggle('active');
this.nextElementSibling.classList.toggle('active');
active = this;
}
items.forEach(item => item.addEventListener('click', toggleAccordion));

Related

adding click event to promise

I am having trouble with adding a click event to an element that only exists if another element on the page has been clicked. This is on the Amazon website just for context. I am trying to add an overlay at the bottom of the mobile screen which allows you to select the quantity you would want and add it to basket.
In order to do this I have created my own dropdown which allows you to select the quantity (selectNumber function) - what this does is replicate the click on the original dropdown and bring up the popover element which you click in order to select the quantity. I then targeted the element in the popover which represents the quantity you would like and added a click event on that too.
The issue I am having is that on the first time I click on my dropdown and change the quantity it replicates the click on the original dropdown but doesn't update the quantity. However, if I try it again after it works perfectly. I have a feeling that it is to do with the fact the popover does not exist until the first click of the dropdown, it then fails to fire the rest of my code. I am using a Promise function (waitforElem) in order to observe that the element now exists on the page. The second time - as the element now exists - it is able to fire the rest of my code, which allows you to update the quantity, just fine. I have tried to put all my code in the same .then function but in the below I have split it into two .then's. Neither seems to work. My code is below:
function waitForElm(selector) {
return new Promise((resolve) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector))
}
const observer = new MutationObserver(() => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector))
observer.disconnect()
}
})
observer.observe(document.body, {
childList: true,
subtree: true,
})
})
}
selectNumber().forEach((el) =>
el.addEventListener('input', () => {
const numberSelected = Number(el.options[el.selectedIndex].value)
console.log(numberSelected)
amazonDropDown().click()
const popoverDropdown = () =>
document.querySelectorAll(
'.a-popover.a-dropdown.a-dropdown-common.a-declarative .a-popover-wrapper .a-nostyle.a-list-link .a-dropdown-item'
)
waitForElm(
'.a-popover.a-dropdown.a-dropdown-common.a-declarative .a-popover-wrapper '
)
.then(() => {
console.log('Element is ready')
})
.then(() => {
document.querySelectorAll(
'.a-popover.a-dropdown.a-dropdown-common.a-declarative .a-popover-wrapper .a-nostyle.a-list-link .a-dropdown-item'
)
const popoverArr = () => Array.from(popoverDropdown())
const popoverEl = () =>
popoverArr().filter((el) => {
const numb = () => Number(el.firstElementChild.textContent)
return numb() === numberSelected
})
console.log(popoverEl())
for (let i = 0, len = popoverEl().length; i < len; i++) {
const firstChild = (): any => popoverEl()[i].firstElementChild
console.log(firstChild())
firstChild().click()
}
})
})
)
I really hope the above makes sense and that someone can help me on this as I have been banging my head on it for a whole day. Thank you in advance.

how i can use toggle and function that remove the class from the previous element together only using JavaScript i have tried to Different ways?

-First Way-
Here I Can't Remove The Previous Elements and add the class for The new One
linksBtnArr.forEach((item, index) => {
item.addEventListener("click", () => {
arrowArr[index].classList.toggle("active");
if (arrowArr[index].classList.contains("active")) {
menuArr[index].style.display = "block";
} else {
menuArr[index].style.display = "none";
}
});
});
-Second Way-
Here I Can remove The previous Element But i Can't use Toggle to Remove The class Form The Same Element That i clicked again
linksBtnArr.forEach((link, i) => {
link.addEventListener("click", () => {
removeActiveClasses();
arrowArr[i].classList.add("active");
});
});
// Remove The previous Menu
function removeActiveClasses() {
arrowArr.forEach((arrow) => {
arrow.classList.remove("active");
});
}
this way
linksBtnArr.forEach( (lk,_,All_lk) => {
lk.onclick =_=> {
if (lk.classList.toggle('active'))
All_lk.forEach( lx=> lx.classList.toggle('active', lk===lx) )
} })
Look At The UL Links That what i'm trying to Do !
https://mohmostafa-web.github.io/blogr-landing-page-frontend-mentor

Make Tab active when clicked with JavaScript

What I am trying to accomplish is making the clicked tab active
I have seen a lot of jQuery examples, but I'm using JavaScript ES6
I know it's an easy task but my mind is so done right now that I can't think
here is the code: https://codepen.io/revatto/pen/wvMzOqo
here is my main.js:
const tabs = document.querySelectorAll('[data-tab-target]');
const tabsContents = document.querySelectorAll('[data-tab-content]');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const target = document.querySelector(tab.dataset.tabTarget);
tabsContents.forEach(tabContent => {
tabContent.classList.remove('active');
})
target.classList.add('active');
})
})
You were only adding and removing 'active' class from tab contents you need to do the same with 'li' i.e. list too.
const tabs = document.querySelectorAll('[data-tab-target]');
const tabsContents = document.querySelectorAll('[data-tab-content]');
tabs.forEach(tab => {
tab.addEventListener('click', (e) => {
const target = document.querySelector(tab.dataset.tabTarget);
tabsContents.forEach(tabContent => {
tabContent.classList.remove('active');
})
target.classList.add('active');
//loop through 'li' items and remove 'active' class
tabs.forEach(tab => {
tab.classList.remove('active');
})
//add 'active' class to clicked 'li' item
e.target.classList.add("active");
})
})

Rewrite jQuery function to Vanilla JS and optimize

First I want to apologize, because I am a bit newbie with JS and it's library jQuery. Basically I have written a javascript function that toggles my hamburger menu:
const menuToggle = document.querySelector('.menu-toggle');
let menuOpen = false;
menuToggle.addEventListener('click', () => {
if(!menuOpen) {
menuToggle.classList.add('open');
menuOpen = true;
} else {
menuToggle.classList.remove('open');
menuOpen = false;
}
});
Now I want to rewrite this jQuery function to Vanilla JS
$(function() {
$('.toggle').on('click', function() {
$('.inner-wrapper').toggleClass('open');
});
});
I tried this, but it is not working:
var searchElement = document.createElement(".inner-wrapper");
document.querySelector(".toggle").appendChild(searchElement);
searchElement.addEventListener("open", handleClick);
I would like to combine both functions if possible. Thanks in advance!
First, this code of yours:
menuToggle.addEventListener('click', () => {
if(!menuOpen) {
menuToggle.classList.add('open');
menuOpen = true;
} else {
menuToggle.classList.remove('open');
menuOpen = false;
}
});
can be simplified to this:
menuToggle.addEventListener('click', () => {
menuToggle.classList.toggle('open');
menuOpen = !menuOpen;
});
Second, the equivalent vanilla JavaScript version of this:
$(function() {
$('.toggle').on('click', function() {
$('.inner-wrapper').toggleClass('open');
});
});
is this:
document.querySelector('.toggle').addEventListener('click', function () {
document.querySelector('.inner-wrapper').classList.toggle('open');
});
Finally, this code of yours has problems:
var searchElement = document.createElement(".inner-wrapper");
document.querySelector(".toggle").appendChild(searchElement);
searchElement.addEventListener("open", handleClick);
There is no HTML element called ".inner-wrapper". You probably wanted to create an element with that class, something like this:
var searchElement = document.createElement("div"); // or any valid tag name
searchElement.className = 'inner-wrapper';
Also, there's no event called "open". You probably want that to be "click", and you probably want to target that to the ".toggle" element (not the '.inner-wrapper' element).
Try this,
searchElement.addEventListener("click", handleClick);
And your current code will add a event listener to inner-wrapper class instead of toggle class.
The javascript representation of your jQuery code has to be something like this
document.querySelector('.toggle').addEventListener('click', function(){
document.querySelector('.inner-wrapper').classList.toggle('open');
})

How to add class with JS

I have this code:
let link = document.querySelectorAll(".menu-left p a");
for (var i =0;i<link.length;i++){
link[i].addEventListener("click", function () {
this.classList.add('clicked')
})
}
I click on a link and I add "clicked" class on <a> tag. My issue is: when I click on the next link I want to remove de previous class, but this now doesn't happen. How to do this? What changes should I add in my code for getting the result?
Save a reference to the previously clicked element, and if it exists, remove the class:
const links = document.querySelectorAll(".menu-left p a");
let lastLink;
for (let i = 0; i<links.length; i++){
links[i].addEventListener("click", function () {
if (lastLink) {
lastLink.classList.remove('clicked');
}
this.classList.add('clicked');
lastLink = this;
})
}
(note that because links is a collection, not a single element, it would probably make more sense to call it links, plural)
let link = document.querySelectorAll(".menu-left p a");
for (var i =0;i<link.length;i++){
link[i].addEventListener("click", function (e) {
e.target.classList.toggle('clicked')
})
}
You could take a functional approach to remove any elements with the clicked class before setting the next link to clicked.
const links = document.querySelectorAll(".menu-left p a");
function clickedLinks() {
return document.querySelectorAll(".clicked");
};
for (let i = 0; i < links.length; i++) {
links[i].addEventListener("click", function () {
clickedLinks().forEach(function(link) {
link.classList.remove('clicked')
});
this.classList.add('clicked');
})
}
Alternative using forEach() in place of the for loop and es6 arrow functions.
const rmClickedClass = () => (
document.querySelectorAll(".clicked").forEach(
(link) => link.classList.remove('clicked')
)
);
document.querySelectorAll(".menu-left p a").forEach((link) => {
link.addEventListener("click", () => {
rmClickedClass();
link.classList.add('clicked');
});
};

Categories