I created a dropdown list Javascript Toggle method. I face a problem a problem. The problem is - After clicking one dropdown, another dropdown still opens. I want others dropdown Will to be closed when I click on dropdown. This happen will be each dropdown. How do I do it?
<html>
<head>
<style>
nav{
width:100%;
height:50px;
background-color:#000;
}
button{
height:50px;
margin-left: 10px;
border:0;
background-color: transparent;
color: #fff;
cursor: pointer;
}
div{
display: none;
width: 100%;
height: 300px;
position: absolute;
}
#myDIV1{
background-color: rgb(0,0,255);
color: #fff;
}
#myDIV2{
background-color: rgb(0,255,0);
color: #000;
}
.show{
display:block;
}
</style>
</head>
<body>
<nav>
<button onclick="myFunction1()">Dropdown1</button>
<button onclick="myFunction2()">Dropdown2</button>
</nav>
<div id="myDIV1">
This Dropdown for Dropdown 1
</div>
<div id="myDIV2">
This Dropdown for dropdown 2
</div>
<script>
function myFunction1() {
var element = document.getElementById("myDIV1");
element.classList.toggle("show");
}
function myFunction2() {
var element = document.getElementById("myDIV2");
element.classList.toggle("show");
}
</script>
</body>
</html>
here is the solution, all the code is commented.
div1 and div2 are hidden by default...
so with .toggle(): https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle
I will add the class .show if there isn't
else I will remove the class from it.
so if the user clicks the first time on the button it will show the div1 then if he reclicked will hide, and this is looped (if he reclick)...
with classList.remove, we will hide the other element (always):
if clicked the button N1 will hide div2
if clicked the button N2 will hide div1
let div1 = document.getElementById("myDIV1");
let div2 = document.getElementById("myDIV2");
function myFunction1() {
div1.classList.toggle("show");
// remove the class for the second div
div2.classList.remove("show");
}
function myFunction2() {
div2.classList.toggle("show");
// remove the class for the first div
div1.classList.remove("show");
}
nav {
width: 100%;
height: 50px;
background-color: #000;
}
button {
height: 50px;
margin-left: 10px;
border: 0;
background-color: transparent;
color: #fff;
cursor: pointer;
}
div {
display: none;
width: 100%;
height: 300px;
position: absolute;
}
#myDIV1 {
background-color: rgb(0, 0, 255);
color: #fff;
}
#myDIV2 {
background-color: rgb(0, 255, 0);
color: #000;
}
/* this is the class we add and remove or toggle with javascript*/
.show {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="./script.js" defer></script>
</head>
<body>
<!-- navbar -->
<nav>
<button onclick="myFunction1()">Dropdown1</button>
<button onclick="myFunction2()">Dropdown2</button>
</nav>
<!-- 1 -->
<div id="myDIV1">
This Dropdown for Dropdown 1
</div>
<!-- 2 -->
<div id="myDIV2">
This Dropdown for dropdown 2
</div>
</body>
</html>
You can make this a little easier to scale by using one function for all dropdown menus. This function closes all open drop-downs and toggles the target one.
function toggleDropDown(id) {
document.querySelectorAll('.dropdown-menu').forEach(el => el.id === id ? el.classList.toggle('show') : el.classList.remove("show"));
}
function toggleDropDown(id) {
document.querySelectorAll('.dropdown-menu').forEach(el => el.id === id ? el.classList.toggle('show') : el.classList.remove("show"));
}
nav {
width: 100%;
height: 50px;
background-color: #000;
}
button {
height: 50px;
margin-left: 10px;
border: 0;
background-color: transparent;
color: #fff;
cursor: pointer;
}
.dropdown-menu {
display: none;
width: 100%;
height: 300px;
position: absolute;
}
#myDIV1 {
background-color: rgb(0, 0, 255);
color: #fff;
}
#myDIV2 {
background-color: rgb(0, 255, 0);
color: #000;
}
.show {
display: block;
}
<nav>
<button onclick="toggleDropDown('myDIV1')">Dropdown1</button>
<button onclick="toggleDropDown('myDIV2')">Dropdown2</button>
</nav>
<div class='dropdown-menu' id="myDIV1">
This Dropdown for Dropdown 1
</div>
<div class='dropdown-menu' id="myDIV2">
This Dropdown for dropdown 2
</div>
Here is the same thing, but instead of hard-coding click events, it's better practice to use eventListeners, which get applied through the script after the page loads, like:
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('nav button').forEach(button => {
button.addEventListener('click', e => {
let id = e.target.dataset.dropdown
document.querySelectorAll('.dropdown-menu').forEach(el => el.id === id ? el.classList.toggle('show') : el.classList.remove("show"));
})
})
})
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('nav button').forEach(button => {
button.addEventListener('click', e => {
let id = e.target.dataset.dropdown
document.querySelectorAll('.dropdown-menu').forEach(el => el.id === id ? el.classList.toggle('show') : el.classList.remove("show"));
})
})
})
nav {
width: 100%;
height: 50px;
background-color: #000;
}
button {
height: 50px;
margin-left: 10px;
border: 0;
background-color: transparent;
color: #fff;
cursor: pointer;
}
.dropdown-menu {
display: none;
width: 100%;
height: 300px;
position: absolute;
}
#myDIV1 {
background-color: rgb(0, 0, 255);
color: #fff;
}
#myDIV2 {
background-color: rgb(0, 255, 0);
color: #000;
}
.show {
display: block;
}
<nav>
<button data-dropdown="myDIV1">Dropdown1</button>
<button data-dropdown="myDIV2">Dropdown2</button>
</nav>
<div class='dropdown-menu' id="myDIV1">
This Dropdown for Dropdown 1
</div>
<div class='dropdown-menu' id="myDIV2">
This Dropdown for dropdown 2
</div>
Related
Please see code below:
const sectionIcon = document.querySelectorAll(".nk-section-icons")
const sectionContainer = document.querySelectorAll(".nk-sec-container")
const sectionIconHover = document.querySelectorAll(".nk-section-icons")
sectionIcon.forEach((sectionBtn)=> {
sectionBtn.addEventListener("click", (btns)=> {
// console.log(sectionIconHover)
const containerTarget = btns.currentTarget.parentElement.children[1]
const containerHoverTarget = btns.currentTarget.parentElement.children[0]
sectionContainer.forEach(items => {
if(items !== containerTarget) {
items.classList.remove("show")
// itemHover.classList.remove("rb")
}
})
sectionIconHover.forEach(itemHover => {
if(itemHover !== containerHoverTarget) {
itemHover.classList.remove("rb")
}
})
containerTarget.classList.toggle("show")
containerHoverTarget.classList.add("rb")
})
})
.nk-section-icons {
height: 30px;
min-width: 30px;
width: 30px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 3px;
background: yellow;
margin-bottom: 5px;
}
.nk-section-icons.rb {
background: black;
}
.nk-sec-container {
width: 300px;
min-width: 300px;
height: 100%;
background: red;
position: absolute;
z-index: 11;
box-shadow: rgba(17, 17, 26, 0.1) 0px 0px 16px;
border-radius: 6px;
left: 70px;
top: 0px;
}
.nk-sec-container.show {
background: green;
}
.nk-section-icons-container {
position: relative;
}
<div class="nk-section-l-icons">
<div class="nk-section-icons-container">
<div class="nk-section-icons fav" data-title="Favorites">btn</div>
<div class="nk-sec-container nk-sec-fav-c">
</div>
</div>
<div class="nk-section-icons-container">
<div class="nk-section-icons recent" data-title="Recent">btn</div>
<div class="nk-sec-container nk-sec-recent-c">
</div>
</div>
<div class="nk-section-icons-container">
<div class="nk-section-icons notifs" data-title="Notifications">btn</div>
<div class="nk-sec-container nk-sec-notif-c">
</div>
</div>
</div>
Hello guys, can you please see my code? Currently, it's working fine when I click the button it's working the classes are moved when I click any of the buttons. However, if I tried clicking the same button again the class rb is not removed but the show class is removed. Can you please help me with how can I fix this? Thanks
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("button").click(function(){
if($("p").hasClass("main"))
{
$("p").toggleClass("main1");
}
else
{
$("p").toggleClass("main");
}
});
});
</script>
<style>
.main {
font-size: 120%;
color: red;
}
.main1 {
font-size: 120%;
color: green;
}
</style>
</head>
<body>
<button>Toggle class "main" for p elements</button>
<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
<p><b>Note:</b> Click the button more than once to see the toggle effect.</p>
</body>
</html>
Use toggling: Toggle between adding and removing the "main" class name for all elements
The toggleClass() method toggles between adding and removing one or more class names from the selected elements.
When the hamburger menu is open, I am trying to allow a click-off screen to toggle the visibility attribute. The other solutions I have seen I do not understand and have been unable to integrate into my code.
The code thus far will either toggle the visibility on click OR toggle visibility when pushing the menu button. With the click off modal event clicking on the screen (but not the menu button) will open and close the modal menu. If you get rid of the click off modal event then the menu button will work.
const navToggle = document.querySelector(".mobile-nav-toggle");
const primaryNavigation = document.querySelector(".primary-navigation");
let dataVisible = false;
//Reference 1: Click off modal
document.addEventListener("click", function () {
if (dataVisible) {
primaryNavigation.toggleAttribute("data-visible");
dataVisible;
}
});
//Reference 2: Menu button
navToggle.addEventListener("click", function () {
primaryNavigation.hasAttribute("data-visible")
? navToggle.setAttribute("aria-expanded", false)
: navToggle.setAttribute("aria-expanded", true);
primaryNavigation.toggleAttribute("data-visible");
dataVisible;
});
dataVisible = function () {
dataVisible = dataVisible ? false : true;
};
.mobile-nav-toggle {
display: none;
}
#media (max-width: 50em) {
.primary-navigation {
display: none;
/* position: fixed; */
position: fixed;
margin-top: 2em;
width: auto;
max-width: 25rem;
padding: var(--size-300);
background: var(--accent-primary);
border-radius: var(--size-100);
box-shadow: 0 0 0 100em rgba(0, 0, 0, 0.486);
}
.navigation::before[data-overlay] {
content: "";
position: fixed;
inset: 0;
background-image: linear-gradient(rgb(0 0 0 / 0), rgb(0 0 0 / 0.8));
}
.nav-list {
display: block !important;
/* gap: var(--size-400) !important; */
text-align: center;
font-weight: var(--fw-bold);
font-size: 1.5rem !important;
}
.nav-list li {
padding: 0.5rem 7ch;
white-space: nowrap;
}
.nav-list li:hover {
background-color: white;
padding: 0.5rem;
border-radius: 5px;
}
.primary-navigation[data-visible] {
display: block;
}
.mobile-nav-toggle {
display: block;
z-index: 100;
cursor: pointer;
background: blue;
border: 0;
padding: 0.5em;
}
.mobile-nav-toggle .icon-close {
display: none;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="section">
<button class="mobile-nav-toggle">Menu</button>
<nav class="primary-navigation" id="primary-navigation">
<div class="nav-logo"> </div>
<div>
<ul role="list" class="nav-list" aria-label="primary">
<li>Services</li>
<li>Work</li>
<li>Blog</li>
<li>About</li>
<li>Buy Now</li>
</ul>
</div>
</nav>
</div>
</body>
</html>
i was wandering how i can hide elements in a div, which has the same class name as another div. i do not want to change the class name, because it will effect my JavaScript. So i want one div acting as like a folder, it is kinda like a drag and drop system. I used a tutorial from Web Dev Simplified. Here is the tutorial if anyone is interested. https://www.youtube.com/watch?v=jfYWwQrtzzY&ab_channel=WebDevSimplified
const draggables = document.querySelectorAll('.draggable')
// create const with class draggable what you can drag
const containers = document.querySelectorAll('.container')
const containerx =document.querySelector('.containerx')
// containers where to drop.
// loop through each pages
draggables.forEach(draggable => {
//DRAGSTRT EVENT
draggable.addEventListener('dragstart',()=> {
draggable.classList.add('dragging')
})//what happends when we start draghging element
// DRAGEND EVENT
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
// Allow elements to be dropped in containers
containers.forEach(container=>{
container.addEventListener('dragover', e =>{
e.preventDefault() // enable dropping remove default
const draggable = document.querySelector('.dragging')
//1 element willl have 'dragging' bc one selected
container.appendChild(draggable)
//SORT
const afterElement = getDragAfterElement(container, e.clientY)
if(afterElement ==null){
container.appendChild(draggable)
}else{
container.insertBefore(draggable,afterElement)
}
})
})
//sorting mouse position
function getDragAfterElement(container, y)
{ //every draggable that we are not dragging
const draggableelements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableelements.reduce((closest,child)=> {
const box = child.getBoundingClientRect()
const offset =(( y - box.top) - box.height) / 2 //mouse position between center boxes
if (offset < 0 && offset > closest.offset)
{
return{offset: offset,element: child}
}else{
return closest
}
},{offset: Number.NEGATIVE_INFINITY}).element
}
body{
margin:0;
background-color: rgb(29, 29, 29);
;
}
/* NAV */
/* NAV STRT */
nav {
list-style-type: none;
font-family:sans-serif;
margin: 0;
padding: 0;
width: 240px;
background-color: #3a3a3a;
height: 100%; /*full height */
position:absolute; /* Make it stick, even on scroll */
overflow: auto; /* enable scrolling if the sidenav has too much content */
left: 0;
font-size: 16px;
}
li a {
display: block; /* links as block elements, whole link area click */
color:white;
padding: 8px 1px; /*padding between pages*/
padding-top:20px;
text-decoration: none;
text-align: center;
background-color: #3a3a3a;
}
li h1,h3{
background-color: #3a3a3a;
}
ul{
list-style-type: none;
background-color: #3a3a3a;
}
/* change link color on hover */
li a:hover {
background-color:#555;
color: #F3ECEC;
}
li:hover {
background-color: aqua;
}
.title {
text-align: center;
color:white;
}
.title1
{
text-align: center;
color:white;
background-color: #3C3939;
padding-top: 20px;
}
.divider{
border-top: 3px solid #bbb;
}
/* NAV END */
/* Nav end */
/* FOLDER CONTAINER */
.container {
background-color: #333;
padding: 1rem;
margin-top: 1rem;
position: relative;
left:0;
min-height: 274px;
max-height: 800px;
min-width:1000px;
display: grid;
grid-template-columns: auto auto auto auto;
overflow-y: scroll;
overflow-x: hidden;
}
.draggable{
background-color:rgba(255, 245, 245, 0.945);
padding: 1rem;
margin-top: 1rem;
border:solid black;
cursor: move; /* change cursor */
width:128px;
height: 174px;
display: grid;
}
.draggable.dragging{
opacity: 0.5;
}
.containerx{
background-color: #3a3a3a;
position: absolute;
left:35%;
top: 30%;
height:600px;
}
.containery{
background-color: #3a3a3a;
width: 1032px;
position: relative;
left:35%;
height:400px;
}
/* END FOLDERS */
/* SCROLLBAR */
/* width */
::-webkit-scrollbar {
width: 20px;
}
/* Track */
::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* SCrollbar end */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="test.css">
<script src="test.js" defer></script>
</head>
<body>
<nav>
<ul>
<li><div class="title"><h1>Organize</h1></div></li>
<li>Folders</li>
<li>Timeline</li>
<li>Pages</li>
<li>Meetings</li>
<li><div class="title1"><h3>documents</h3></div></li>
<li></li>
</ul>
</nav>
<div class="containery">
<div class="container" id="hidden" name="test">
</div>
</div>
<div class="containerx">
<div class="container">
<p class="draggable" draggable="true">3</p>
<p class="draggable" draggable="true">4</p>
<p class="draggable" draggable="true">1</p>
<p class="draggable" draggable="true">2</p>
<p class="draggable" draggable="true">3</p>
<p class="draggable" draggable="true">4</p>
<p class="draggable" draggable="true">1</p>
<p class="draggable" draggable="true">2</p>
</div>
</div>
</body>
</html>
I'm trying to hide and show .sub-menu by clicking the .menu anchor, it works only by showing it. If I press to hide it has no reaction, what I am supposed to do and what is the mistake?
Here is the code.
HTML:
<div class="menu">
<a href="javascript:myFunction();" class="nav" onclick="myFunction()">
<div class="unu"></div>
<div class="doi"></div>
<div class="trei"></div>
</a>
<div id="sm">
FAQ
Support
Features
</div>
</div>
CSS:
nav div{
height:7px;
background-color: white;
margin: 5px 0;
border-radius: 25px;
}
.menu {
position: absolute;
display:inline-block;
text-align:center;
right:130px;
top:60px;
}
.unu {
width: 45px;
}
.doi {
width: 20px;
}
.trei {
width:35px;
}
#sm {
display: none;
}
JS:
function myFunction() {
var x = document.getElementById("sm")
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
If you use
console.log(`${x.style.display}`)
you will get
<empty string>
in your first attempt not
none
that is why you need to set
function myFunction() {
var x = document.getElementById("sm")
if (x.style.display === "" || x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
I will add a console.log in window.load event to see what is the value of the sm element when the page load at first. And you can also check what is new properties when you click button.
const myDiv = document.getElementById('sm');
const controlButton = document.querySelector('button')
window.addEventListener('load', () => {
console.log(`${myDiv.style.display}`);
})
controlButton.addEventListener('click', () => {
if (myDiv.style.display === "" || myDiv.style.display === "none") {
myDiv.style.display = "block";
} else {
myDiv.style.display = "none";
}
console.log(`${myDiv.style.display}`);
})
*,
*::before,
*::after{
box-sizing: border-box;
}
body {
font-family: sans-serif;
min-height: 100vh;
margin: 0;
background-color: bisque;
}
nav div{
height:7px;
background-color: white;
margin: 5px 0;
border-radius: 25px;
}
.menu {
position: absolute;
display:inline-block;
text-align:center;
right:130px;
top:60px;
}
.unu {
width: 45px;
}
.doi {
width: 20px;
}
.trei {
width:35px;
}
#sm {
display: none;
}
button{
position: absolute;
width: 5rem;
height: 5rem;
background-color: greenyellow;
top: 5rem;
left: 5rem;
}
<div class="menu">
<div class="unu"></div>
<div class="doi"></div>
<div class="trei"></div>
</a>
<div id="sm">
FAQ
Support
Features
</div>
</div>
<button>Click me</button>
Instead of using display property you can create a .class for toggling which is quite simple than checking styles.
const toggle = document.querySelector('.toggle');
const hidden = document.querySelector('.hidden');
toggle.addEventListener('click', event => {
// because anchor's default behaviour is redirecting
event.preventDefault();
hidden.classList.toggle('revealed');
});
* {
margin: 0;
padding: 0;
outline: 0;
box-sizing: border-box;
}
body {
height: 100vh;
display: grid;
place-items: center;
}
.toggle,
.hidden {
font-family: sans-serif;
font-size: 1.5rem;
}
.hidden {
position: absolute;
margin-top: 6rem;
display: none;
}
.hidden.revealed {
display: block;
}
Click Me For Toggling
<p class="hidden"> I was hidden Initially </p>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Help</title>
<style>
nav div{
height:7px;
background-color: white;
margin: 5px 0;
border-radius: 25px;
}
.menu {
position: absolute;
display:inline-block;
text-align:center;
right:130px;
top:60px;
}
.unu {
width: 45px;
}
.doi {
width: 20px;
}
.trei {
width:35px;
}
.sm {
display: none;
}
.show {
display:block;
}
</style>
</head>
<body>
<div class="menu">
<a href="#">
<div class="unu">lll</div>
<div class="doi"></div>
<div class="trei"></div>
</a>
<div class="sm">
FAQ
Support
Features
</div>
</div>
<script>
var show = document.querySelector("a")
var x = document.querySelector(".sm")
show.addEventListener('click',()=>{
x.classList.toggle("show")
})
</script>
</body>
</html>
I made a lot of changes! You first I use Js Vanilla j. I relied on the "click" event on the "a" tag
By leaving your function in the link, it works but, once it appears, it disappears by itself again. Which means it doesn't overwrite the display property that was in the class sm.
In short, it works well; you can check on snipett
I have built an accordion which I can add dynamically from an input and everything works fine except when I click on accordion heading text it doesn't work and also when I click on the chevron icon on the right side I get an error!! I am not sure why this happening. if I click on an empty space area it just works fine without any error. you can check the demo & code here on codepen -> https://codepen.io/tauhidul-islam/pen/eYZBzLY
Also here is some screenshot so you can understand. please let me understand what's happening and why. Thank you.
const addForm = document.querySelector(".add");
const list = document.querySelector(".section-list");
// Template Generator Function
const generateTemplate = (section) => {
let html = `
<div class="accordion">
<span>${section}</span>
<i class="fa fa-chevron-down"></i>
</div>
<div class="panel">
<span>Hey there you did it! :-)</span>
</div>
`;
list.innerHTML += html;
// accordion Selector
const accordion = document.querySelectorAll(".accordion");
// Show/Hide accordion Content on Click
for (i = 0; i < accordion.length; i++) {
accordion[i].addEventListener("click", (e) => {
let panel = e.target.nextElementSibling;
if (panel.classList.contains("panel")) {
panel.classList.toggle("active");
}
});
}
};
// Add Section
addForm.addEventListener("submit", (e) => {
e.preventDefault();
const section = addForm.add.value.trim();
if (section.length) {
generateTemplate(section);
addForm.reset();
}
});
.container {
width: 960px;
margin: auto;
}
.add-input {
padding: 15px;
border: 1px solid #dadada;
}
.add-btn {
background: white;
padding: 15px 25px;
margin-bottom: 10px;
border: 1px solid #dadada;
cursor: pointer;
}
/* Accordian Panel */
.accordion {
display: flex;
justify-content: space-between;
background: #03a9f4;
color: white;
padding: 15px;
box-shadow: 0px 0px 4px 0px #dadada;
cursor: pointer;
}
.panel {
display: none;
background-color: white;
padding: 15px;
}
.active {
display: block;
}
<div class="container">
<!-- Add Section -->
<form class="add">
<input type="text" name="add" class="add-input">
<button type="submit" class="add-btn">Add Section</button>
</form>
<!-- Section List -->
<div class="section-list"></div>
</div>
Because you are using e.target in the click event of the generated div, that will reference the template span when you click on the text and the div when you click on the blue bar, so .nextElementSibling won't always point to the same element. Instead, you want to always be calling .nextElementSibling on the div. This can be accomplished by using this.nextElementSibling, however because you are also using an arrow function, this binding won't correctly reference the element that received the event (the div), so if you change to using an anonymous function and this, it works.
const addForm = document.querySelector(".add");
const list = document.querySelector(".section-list");
// Template Generator Function
const generateTemplate = (section) => {
let html = `
<div class="accordion">
<span>${section}</span>
<i class="fa fa-chevron-down">^</i>
</div>
<div class="panel">
<span>Hey there you did it! :-)</span>
</div>
`;
list.innerHTML += html;
// accordion Selector
const accordion = document.querySelectorAll(".accordion");
// Show/Hide accordion Content on Click
for (i = 0; i < accordion.length; i++) {
// Use an anonymous function for the event listener so that
// "this" will bind to the element that recieved the event,
// which is the `div` in this case.
accordion[i].addEventListener("click", function(e) {
// We don't want to reference the element that triggered the event
// because that might be the span or the div and you won't always get
// the correct reference with .nextElementSibling. We always want to
// start from the div, which recieves the event.
let panel = this.nextElementSibling;
if (panel.classList.contains("panel")) {
panel.classList.toggle("active");
}
});
}
};
// Add Section
addForm.addEventListener("submit", (e) => {
e.preventDefault();
const section = addForm.add.value.trim();
if (section.length) {
generateTemplate(section);
addForm.reset();
}
});
.container {
width: 960px;
margin: auto;
}
.add-input {
padding: 15px;
border: 1px solid #dadada;
}
.add-btn {
background: white;
padding: 15px 25px;
margin-bottom: 10px;
border: 1px solid #dadada;
cursor: pointer;
}
/* Accordian Panel */
.accordion {
display: flex;
justify-content: space-between;
background: #03a9f4;
color: white;
padding: 15px;
box-shadow: 0px 0px 4px 0px #dadada;
cursor: pointer;
}
.panel {
display: none;
background-color: white;
padding: 15px;
}
.active {
display: block;
}
<div class="container">
<!-- Add Section -->
<form class="add">
<input type="text" name="add" class="add-input">
<button type="submit" class="add-btn">Add Section</button>
</form>
<!-- Section List -->
<div class="section-list"></div>
</div>
Without the loop for assigning the click handlers:
const addForm = document.querySelector(".add");
const list = document.querySelector(".section-list");
const expand = (element) => {
let panel = element.nextElementSibling;
if (panel.classList.contains("panel")) {
panel.classList.toggle("active");
}
};
// Template Generator Function
const getAccordionItem = (section) => {
let html = `
<div class="accordion" onclick="expand(this)">
<span>${section}</span>
<i class="fa fa-chevron-down"></i>
</div>
<div class="panel">
<span>Hey there you did it! :-)</span>
</div>
`;
return html;
};
// Add Section
addForm.addEventListener("submit", (e) => {
e.preventDefault();
const section = addForm.add.value.trim();
if (section.length) {
list.innerHTML += getAccordionItem(section);
addForm.reset();
}
});
body {
margin: 50px 0;
background-color: #f2f2f2;
font-family: Arial, Helvetica, sans-serif;
}
.container {
width: 960px;
margin: auto;
}
.add-input {
padding: 15px;
border: 1px solid #dadada;
}
.add-btn {
background: white;
padding: 15px 25px;
margin-bottom: 10px;
border: 1px solid #dadada;
cursor: pointer;
}
/* Accordian Panel */
.accordion {
display: flex;
justify-content: space-between;
background: #03a9f4;
color: white;
padding: 15px;
box-shadow: 0px 0px 4px 0px #dadada;
cursor: pointer;
}
.panel {
display: none;
background-color: white;
padding: 15px;
}
.active {
display: block;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Accordian</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<!-- Add Section -->
<form class="add">
<input type="text" name="add" class="add-input">
<button type="submit" class="add-btn">Add Section</button>
</form>
<!-- Section List -->
<div class="section-list"></div>
</div>
<script src="app.js"></script>
</body>
</html>