Vanilla JS Accordion - javascript

Given the code below, how can I add functionality that hides active items when clicking on another? I'd like only one item to be open at a time.
const toggleElements = document.querySelectorAll('.accordion__item');
toggleElements.forEach(el => {
el.addEventListener('click', function() {
this.classList.toggle('is-active');
});
});

Close all others, then open current.
const toggleElements = document.querySelectorAll('.accordion__item');
toggleElements.forEach(el => {
el.addEventListener('click', function() {
toggleElements.forEach(el => el.classList.remove('is-active'));
this.classList.add('is-active');
});
});

inline comments
toggleElements.forEach((el) => {
el.addEventListener("click", function () {
// check whether already active or not
const isActive = this.classList.contains("is-active");
// reset to collapse all including the current item
toggleElements.forEach((el) => el.classList.remove("is-active"));
// if collapsing from expand, it is already in above loop
// if intended to expand (from collapse)
if (!isActive) {
this.classList.add("is-active");
}
});
});

Related

How can I active a link when click, and deactivate the others in javascript?

I'm trying to handle just one element when I click in the link, but when I click on another it doesn't remove the active to other links. Can anyone help me ?
Here's my code . . .
let parentT = document.querySelector('.menu-item'); //PARENT
let allEl = parentT.querySelectorAll('a'); // ELEMENTS INSIDE PARENT
allEl.forEach(elem =>{
elem.addEventListener('click', (event) => {
event.preventDefault();
event.target.setAttribute('class', 'active');
if(event.target.classList == "active") {
console.log(event.target);
}
});
});
I've try some methods but with no result.
I think this is what you wanted.
this code loops through all anchor elements when one is clicked and removes that active class from all of them, then adds the active class to the clicked element.
let parentT = document.querySelector('.menu-item'); //PARENT
let allEl = parentT.querySelectorAll('a'); // ELEMENTS INSIDE PARENT
allEl.forEach(elem =>{
elem.addEventListener('click', (event) => {
event.preventDefault();
allEl.forEach(el => {
el.classList.remove('active');
});
event.target.classList.add('active');
});
});
.active {
color: red;
}
<div class="menu-item">
Link 1
Link 2
Link 3
Link 4
Link 5
</div>

Remove active class "classList.remove" in Javascript

I have javascript code that adds class to the div panel-s-c-3 on mouseover. So if I hover the item, it will add class active-s-c-cardexp. I need to remove that class after I leave the item area. This is what I have. Will be happy for any suggestions..
const panels2 = document.querySelectorAll(".panel-s-c-3");
panels2.forEach((panel2) => {
panel2.addEventListener("mouseover", () => {
removeActiveclasses2();
panel2.classList.add("active-s-c-cardexp");
});
});
function removeActiveclasses2() {
panels2.forEach((panel2) => {
panel2.classList.remove("active-s-c-cardexp");
});
}
you can remove active class on mouseleave event as below:
panel2.addEventListener("mouseover", () => {
panel2.classList.add("active-s-c-cardexp");
});
panel2.addEventListener("mouseleave", () => {
removeActiveclasses2();
});
You can try using mouseenter to add class active-s-c-cardexp and then mouseleave to remove the class

addEventListener for only one time on any one element in forEach loop javascript

I have an array of NodeList. I loop through every NodeList element and add click event Listener.
var itemsList = document.querySelectorAll('.items');
itemList.forEach(item => {
item.addEventListener('click', () => {
console.log('clicked');
})
})
As soon as I clicked any one of the items I want to remove the event listener for all the other items as well.
It doesn't matter each item is clicked or not.
To do it directly without jQuery or anything, and without overcomplicating it, you could do something like this:
const itemsList = document.querySelectorAll('.items');
const onClick = () => {
console.log('clicked');
itemsList.forEach(item => {
item.removeEventListener('click', onClick);
});
};
itemsList.forEach(item => {
item.addEventListener('click', onClick);
});
Basically you keep a reference to the click function, and the function itself removes itself from all nodes in the list.
If you want to know which item was clicked, you can add a parameter to the onClick function, which will be the click event, from which you can get the item that was clicked, like so:
const itemsList = document.querySelectorAll('.items');
const onClick = event => {
const clickedItem = event.target
console.log('clicked on ' + clickedItem.textContent);
itemsList.forEach(item => {
item.removeEventListener('click', onClick);
});
};
itemsList.forEach(item => {
item.addEventListener('click', onClick);
});
Something along these lines will let you get a reference to which item was actually clicked.
This is very concise using jquery
var handler=function(){
//your logic of click event
alert('clicked')
}
// handle click and add class
$('.items').on("click", function(){
handler();
$('.items').not(this).off("click");
})
This will removes every other click events after clicking the first one. If you want to remove every event including the first clicked, then remove the not() method from my code
One approach is to add the listener to an ancestor of your elements i.e. document + a target check on which element was clicked, if you need to remove this listener you only have to remove it once
function handleClick(event) {
if (event.target.classList.contains("items")) {
alert(event.target.textContent)
document.removeEventListener('click', handleClick)
}
}
document.addEventListener('click', handleClick);
<div id="app">
<li class="items">1</li>
<li class="items">2</li>
<li class="items">3</li>
<li class="items">4</li>
<li class="items">5</li>
</div>
You can make this more expressive
var itemsList = document.querySelectorAll('.items');
const updateClickListener = (list, callback, operation = 'add') => {
itemList.forEach(item => {
item[`${operation}EventListener`]('click', () => {
callback();
updateClickListener(list, () => {}, 'remove');
})
})
}
updateClickListener(
list,
() => {
console..log('clicked');
});

Closing a dropdown navbar on click in JavaScript?

I've implemented a navbar using Bulma but I'm having trouble getting it to close after the user has clicked on an element inside the navbar. I have a single page setup so the page does not refresh, therefore the navbar doesn't "reset" and close.
Sorry if the answer is obvious or simple, I'm very new to JavaScript. I've attached a snippet of the code below.
document.addEventListener('DOMContentLoaded', () => {
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) {
// Add a click event on each of them
$navbarBurgers.forEach( el => {
el.addEventListener('click', () => {
// Get the target from the "data-target" attribute
const target = el.dataset.target;
const $target = document.getElementById(target);
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active');
$target.classList.toggle('is-active');
});
});
}
});
Any help would be greatly appreciated, thank you!
When elements get added to the DOM (in your case the .navbar-burgers) then you need to reattach the listeners. You could wrap your above javascript in a function and call it again when the content of your site has changed.
Or you specify a parent element which listens to a click event and check if the clicked target inside of it has a class .navbar-burger. E.g.:
document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('.navbar-burger')) {
document.querySelector('body').addEventListener('click', (el) => {
if (el.currentTarget.classList.contains('.navbar-burger')) {
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active');
document.getElementById(el.getAttribute('data-target')).classList.toggle('is-active');
}
});
}
});
In this example I specified body but I recommend you to be more specific. Because every click on your site will get into this event and checks if it is a navigation thing. However, the first option to reattach the listeners is a better practice.
I added an event listener on every navbar item to close the burger menu
item.addEventListener("click", () => {
document.getElementById(burger_el.dataset.target).classList.remove('is-active');
$target.classList.remove('is-active');
});
Here is the complete code I use to add the listeners for the burger menu:
document.addEventListener('DOMContentLoaded', () => {
const navbarBurgers = document.querySelectorAll('.navbar-burger');
const navbarItems = document.querySelectorAll(".navbar-item");
navbarBurgers.forEach(burger_el => {
burger_el.addEventListener('click', () => {
// Toggle burger-menu
document.getElementById(burger_el.dataset.target).classList.toggle('is-active');
$target.classList.toggle('is-active');
});
navbarItems.forEach(item => {
item.addEventListener("click", () => {
// Close burger-menu
document.getElementById(burger_el.dataset.target).classList.remove('is-active');
$target.classList.remove('is-active');
});
});
});
});

Why is my function appending to all elements it has previously appended to?

I have the following code:
// Show a popup box and return the value
function showPopup(callback) {
$('.modal').show();
$('.save').on('click', () => {
$('.modal').hide();
callback($('input').val());
});
}
// Add a new option to a select element
function addToDropdown(dropdownSelector) {
showPopup((newValue) => {
$(dropdownSelector).append(
`<option value=${newValue}>${newValue}</option>`
);
});
}
$('#btn1').on('click', () => {
addToDropdown('#select1');
});
$('#btn2').on('click', () => {
addToDropdown('#select2');
});
When I click #btn1 and enter a new value, it is added to #select1.
Then I click #btn2 and enter a new value, it is added to #select1 and #select2.
Then if I click #btn1 and enter a new value, it is added to #select1 twice and #select2.
Why is this happening? It's like the function is being called for each time it's been called in the past.
$('.save').on('click', () => {
is adding the click event each time when showPopup(callback) is called.
You can unbind the previous click event(if any) and attach it like the following:
function showPopup(callback) {
$('.modal').show();
$('.save').unbind( "click" );
$('.save').bind('click', () => {
$('.modal').hide();
callback($('input').val());
});
}
Another idea(not a very good one, but quick-easy one) you can use is instead of appending you can replace the html in the dropdown:
function addToDropdown(dropdownSelector) {
showPopup((newValue) => {
$(dropdownSelector).html( $(dropdownSelector).html() + `<option value=${newValue}>${newValue}</option>` );
});
}

Categories