Angular JS - HTML Accordion breaking in ng-repeat - javascript

I need some little help with some template I'm working on for my project.
I found this code for an accordion container on the internet:
/* Toggle between adding and removing the "active" and "show" classes when the user clicks on one of the "Section" buttons. The "active" class is used to add a background color to the current button when its belonging panel is open. The "show" class is used to open the specific accordion panel */
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function () {
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
/* Style the buttons that are used to open and close the accordion panel */
button.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: 1px solid lightgray;
outline: none;
transition: 0.4s;
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
button.accordion.active, button.accordion:hover {
background-color: #ddd;
}
/* Style the accordion panel. Note: hidden by default */
div.panel {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: 0.6s ease-in-out;
opacity: 0;
}
/* The "show" class is added to the accordion panel when the user clicks on one of the buttons. This will show the panel content */
div.panel.show {
opacity: 1;
max-height: 500px;
}
button.accordion:after {
content: "+"; /* Unicode character for "plus" sign (+) */
font-size: 13px;
color: #777;
float: right;
margin-left: 5px;
}
button.accordion.active:after {
content: "-"; /* Unicode character for "minus" sign (-) */
}
<button class="accordion">Section 1</button>
<div class="panel">
<p>Lorem ipsum...</p>
</div>
<button class="accordion">Section 2</button>
<div class="panel">
<p>Lorem ipsum...</p>
</div>
<button class="accordion">Section 3</button>
<div class="panel">
<p>Lorem ipsum...</p>
</div>
It works fine, looks nice, exactly what I wanted. But when I try to do something like this:
<div ng-repeat="category in categories">
<button class="accordion">{{category.name}}</button>
<div class="panel">
<p>{{category.description}}</p>
</div>
</div>
the tabs don't open anymore. I can't find the problem. Can anyone give me a solution to this?
Thank you.

Here is a different approach to do this:
Add ng-click to the button to toggle the class active of him.
Change the .show selector to button.active + div.panel so you can change only the button class.
angular.module('app', []).controller('ctrl', function($scope) {
$scope.categories = [];
for (var i = 1; i <= 3; i++) {
$scope.categories.push({
name: 'cat' + i,
description: 'description' + i
});
}
});
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
button.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
button.accordion.active, button.accordion:hover {
background-color: #ddd;
}
div.panel {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: 0.6s ease-in-out;
opacity: 0;
}
button.active + div.panel {
opacity: 1;
max-height: 500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<h2>Animated Accordion</h2>
<div ng-app="app" ng-controller="ctrl">
<div ng-repeat="category in categories">
<button class="accordion" data-ng-click="category.active = !category.active" data-ng-class="{'active': category.active}">{{category.name}}</button>
<div class="panel">
<p>{{category.description}}</p>
</div>
</div>
</div>

var app = angular.module('app',[]);
app.controller('mycontroller',function($scope){
$scope.categories = [{name:'Lorem ipsum...'},{name:'Lorem ipsum...'},{name:'Lorem ipsum...'}]
});
<html ng-app='app'>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script>
</head>
<body ng-controller='mycontroller'>
<div ng-repeat="category in categories">
<button ng-class="accordion">{{category.name}}</button>
<div ng-class="panel">
<p>{{category.description}}</p>
</div>
</div></body>
</html>

Related

HTML collapsible closed by default

I am currently working on a website and am currently up to adding collapsibles.
I am having some difficulty with collapsibles as shown:
body {
align-self: center;
}
h1, p , body > ul, ol{
text-align: center;
align-self: center;
background-color: rgba(255, 255, 255, 0.6);
width: 40%;
margin: 20px 29%;
padding: 5px 3%;
}
h1 {
font-family: 'Press Start 2P';
font-size: 40px;
}
p, ul, ol {
font-family: "Turret Road";
font-size: 20px;
}
.collapsible {
background-color: #eee;
color: #000;
cursor: pointer;
padding: 8px;
width: 90%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
margin: 10px 5%;
}
.active, .collapsible:hover {
background-color: rgba(255, 255, 255, 1);
}
<body>
<button type="button" class="collapsible"><h1>Basic Rundown</h1></button>
<div class="content">
<p>Vaporfest is inspired by internet music genres like vaporwave, future funk and eccojams.</p>
<p>At Vaporfest, several artists will be performing music that will take you back to a better time.</p>
</div>
<button type="button" class="collapsible"><h1>What to bring</h1></button>
<div class="content">
<ol>
<li>Enough clothes for 5 days</li>
<li>Recording equipment (if you want)</li>
<li>Basic toiletries</li>
<li>Anything else you think is neccesary</li>
</ol></div>
<button type="button" class="collapsible"><h1>Security measures</h1></button>
<div class="content">
<p>Attendees will be tested for COVID every day.</p>
<p>You have to be older than 18 to attend. </p>
</div>
<button type="button" class="collapsible"><h1>Rules about camping</h1></button>
<div class="content">
<ol>
<li>Be respectful at all times.</li>
</ol></div>
<button type="button" class="collapsible"><h1>Prohibited items</h1></button>
<div class="content">
<ol>
<li>Cutlery or hard plates</li>
<li>Metal blades</li>
<li>Gas tanks, gas cylinders, jerry cans</li>
<li>Glass</li>
<li>BBQ sets</li>
<li>Unperscribed stimulants</li>
</ol></div>
<button type="button" class="collapsible"><h1>Getting there</h1></button>
<div class="content">
<p>The festival site is roughly 1 hour away from Kansai International Airport.</p>
</div>
<script>
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
</script>
</body>
When the page loads, the collapsibles are open by default, which isn't desired. I want it to be closed by default.
How do I make the collapsibles closed by default when the page is loaded?
Try using JavaScript and Jquery
<body onload=“$("#myModal").modal('hide');”>
add this CSS:
.content {
display: none;
}

HTML two-level accordion menu hiding and not adding content

I'm trying to do a two level accordion menu. The first level works fine but when I add the second level, the second level content hiddes in the first level Section 2 title. Someone knows how can I solve this?
My html code is this :
<div class="container-wrapper"></div>
<button class="accordion"><p class="title-sections">Provisioning</p></button>
<div class="panel">
<button class="sub-accordion">Set Variables</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Get Variables</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Reset</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Get Base Report</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
</div>
<button class="accordion"><p class="title-sections">Availability</p></button>
<div class="panel">
<p>Lorem ipsum...</p>
</div>
My CSS :
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
}
.sub-accordion {
background-color: rgb(181, 255, 191);
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: center;
border: none;
outline: none;
transition: 0.4s;
}
.active, .accordion:hover {
background-color: #ccc;
}
.sub-active, .sub-accordion:hover {
background-color: rgb(104, 255, 124);;
}
.panel {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.sub-panel {
padding: 0 18px;
background-color: lightblue;
max-height: 0;
overflow: hidden;
transform: max-height 0.2s ease-out;
}
.container-wrapper {
padding: 20pt;
}
.title-sections {
font-size: 20px;
}
And finally my JS, here is the problem. I copy pasted the same code of the first level accordion to make the second level accordion and there's a problem with the height but I don't quite know which part needs to be changed :
var acc = document.getElementsByClassName("accordion");
var i;
var sub_acc = document.getElementsByClassName("sub-accordion");
var j;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
for (j = 0; j < sub_acc.length; j++) {
sub_acc[j].addEventListener("click", function() {
this.classList.toggle("sub-active");
var sub_panel = this.nextElementSibling;
if (sub_panel.style.maxHeight) {
sub_panel.style.maxHeight = null;
} else {
sub_panel.style.maxHeight = sub_panel.scrollHeight + "px";
}
});
}
Here is the fix in Javascript. The solution is just looking for the active accordion and setting its max height again in sub-accordion click logic
const activeAccordion = document.getElementsByClassName("accordion active")[0]
var panel = activeAccordion.nextElementSibling;
panel.style.maxHeight = panel.scrollHeight + "px";
Fully integrated with your Javascript
var acc = document.getElementsByClassName("accordion");
var i;
var sub_acc = document.getElementsByClassName("sub-accordion");
var j;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
for (j = 0; j < sub_acc.length; j++) {
sub_acc[j].addEventListener("click", function() {
this.classList.toggle("sub-active");
var sub_panel = this.nextElementSibling;
if (sub_panel.style.maxHeight) {
sub_panel.style.maxHeight = null;
} else {
sub_panel.style.maxHeight = sub_panel.scrollHeight + "px";
}
//The change is here
const activeAccordion = document.getElementsByClassName("accordion active")[0]
var panel = activeAccordion.nextElementSibling;
panel.style.maxHeight = panel.scrollHeight + "px";
});
}
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
}
.sub-accordion {
background-color: rgb(181, 255, 191);
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: center;
border: none;
outline: none;
transition: 0.4s;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.sub-active,
.sub-accordion:hover {
background-color: rgb(104, 255, 124);
;
}
.panel {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.sub-panel {
padding: 0 18px;
background-color: lightblue;
max-height: 0;
overflow: hidden;
transform: max-height 0.2s ease-out;
}
.container-wrapper {
padding: 20pt;
}
.title-sections {
font-size: 20px;
}
<div class="container-wrapper"></div>
<button class="accordion"><p class="title-sections">Provisioning</p></button>
<div class="panel">
<button class="sub-accordion">Set Variables</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Get Variables</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Reset</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Get Base Report</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
</div>
<button class="accordion"><p class="title-sections">Availability</p></button>
<div class="panel">
<p>Lorem ipsum...</p>
</div>
For Your HTML you need to wrap the whole thing in container-wrapper here's the code for it:
<div class="container-wrapper">
<button class="accordion">
<p class="title-sections">Provisioning</p>
</button>
<div class="panel">
<button class="sub-accordion">Set Variables</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Get Variables</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Reset</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
<button class="sub-accordion">Get Base Report</button>
<div class="sub-panel">
<p>Lorem ipsum...</p>
</div>
</div>
<button class="accordion">
<p class="title-sections">Availability</p>
</button>
<div class="panel">
<p>Lorem ipsum...</p>
</div>
</div>
and for your CSS:
.container-wrapper {
padding: 20pt;
//add these two lines
display: flex;
flex-direction: column;
}

How can i select a specific element after i've clicked another element in the same div [duplicate]

This question already has answers here:
Cleanest way to get the next sibling in jQuery
(3 answers)
Closed 2 years ago.
I have a h3 element and a p element which are inside a div element ,like this:
<div class="question">
<h3> *a question* <img class="arrow" src="" alt="an-arrow-img"> </img> </h3>
<p> *an answer* </p>
</div>
And I have a class named "show" in my css file, which looks like this:
//shows the answer when I click the h3 element
.show{
display: block;
}
on the website, I'm trying to make the questions-answers look like this:
show-hide p element
I've used javascript to toggle the class "show" when I click the questions (h3 elements) but I toggle them all and can't figure out how i can select the one I've clicked. My javascript code is this one till now:
$("h3").on("click", function(){
$("p").toggleClass("show");
});
Is it my HTML structure that's wrong, or is there a way to combine the $(this) selector to show the answer only to the question I've clicked?
var question = document.getElementsByClassName("question");
var i;
for (i = 0; i < question.length; i++) {
question[i].addEventListener("click", function() {
this.classList.toggle("active");
var answer = this.nextElementSibling;
if (answer.style.maxHeight) {
answer.style.maxHeight = null;
} else {
answer.style.maxHeight = answer.scrollHeight + "px";
}
});
}
.question {
background-color: #2d6596;
color: #f1f1f1;
cursor: pointer;
padding: 18px;
width: 100%;
border: 1px solid white;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.active, .question:hover {
background-color: #1a364f;
}
.question:after {
content: '\002B';
color: #f1f1f1;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "\2212";
}
.answer {
padding: 0 18px;
background-color: #f2f2f2;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
<button class="question">The question</button>
<div class="answer">
<p>The answer</p>
</div>
<button class="question">The question</button>
<div class="answer">
<p>The answer</p>
</div>
<button class="question">The question</button>
<div class="answer">
<p>The answer</p>
</div>
Use JQuery.next() to select sibling of $this.
$("h3").on("click", function(){
$(this).next("p").toggleClass("show");
});

Accordion body by link

The content bodies of an accordion open by clicking on panels. This is usually what's best. What I am trying to do now is change this behavior so that the content bodies only get revealed by clicking on a link included in one of the panels (or anywhere else, really). Hope that makes sense.
First of all: Is this possible?
This is the code right now:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.accordion {
background-color: #fff;
color: #444;
cursor: pointer;
width: 103%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
margin: -5px;
}
.bg {
width: 100%;
}
.active,
.accordion:hover {
background-color: #fff;
}
.panel {
padding: 50px 0px;
display: none;
width: 100%;
background-color: white;
overflow: hidden;
border-style: groove;
}
.accordion.active+div {
display: block
}
.column {
float: left;
width: 20%;
padding: 10px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
</style>
</head>
<body>
<button class="accordion"><div class="parent">Only the following link should open the content panel: link_1</button>
<div class="panel">
<p>Content 1</p>
</div>
<button class="accordion">Only the following link should open the content panel: link_2</button>
<div class="panel">
<p>Content 2</p>
</div>
<button class="accordion">Only the following link should open the content panel: link_3</button>
<div class="panel">
<p>Content 3</p>
</div>
<button class="accordion">Only the following link should open the content panel: link_4</button>
<div class="panel">
<p>Content 4</p>
</div>
<script>
function scrollElmVert(el,num) { // to scroll up use a negative number
var re=/html$/i;
while(!re.test(el.tagName) && (1 > el.scrollTop)) el=el.parentNode;
if(0 < el.scrollTop) el.scrollTop += num;
}
var acc = document.getElementsByClassName("accordion");
var i;
var open = null;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
if (open == this) {
open.classList.toggle("active");
open = null;
} else {
if (open != null) {
open.classList.toggle("active");
}
this.classList.toggle("active");
open = this;
//Scroll to clicked element
open.scrollIntoView();
scrollElmVert(open,-68);
}
});
}
</script>
</body>
</html>
Few changes to the css, but overall its an html issue. You will need to have two different elements or at least different classes to distiguish between the one that opens your accordion and the one that doesn't
Hope this is what you were looking for. Happy to explain or help in a better solution if needed.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.accordion {
background-color: #fff;
color: #444;
cursor: pointer;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
margin: -5px;
display: inline;
}
.not-accordion {
color: #444;
font-size: 15px;
display: inline-block;
}
.bg {
width: 100%;
}
.active,
.accordion:hover {
background-color: #fff;
}
.panel {
padding: 50px 0px;
display: none;
width: 100%;
background-color: white;
overflow: hidden;
border-style: groove;
}
.accordion.active+div {
display: block
}
.column {
float: left;
width: 20%;
padding: 10px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
</style>
</head>
<body>
<div class="parent">
<p class='not-accordion'>Only the following link should open the content panel:</p> <button class="accordion">link_1</button>
<div class="panel">
<p>Content 1</p>
</div>
<p class='not-accordion'>Only the following link should open the content panel:</p> <button class="accordion">link_2</button>
<div class="panel">
<p>Content 2</p>
</div>
<p class='not-accordion'>Only the following link should open the content panel:</p> <button class="accordion">link_3</button>
<div class="panel">
<p>Content 3</p>
</div>
<p class='not-accordion'>Only the following link should open the content panel:</p> <button class="accordion">link_4</button>
<div class="panel">
<p>Content 4</p>
</div>
<script>
function scrollElmVert(el, num) { // to scroll up use a negative number
var re = /html$/i;
while (!re.test(el.tagName) && (1 > el.scrollTop)) el = el.parentNode;
if (0 < el.scrollTop) el.scrollTop += num;
}
var acc = document.getElementsByClassName("accordion");
var i;
var open = null;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
if (open == this) {
open.classList.toggle("active");
open = null;
} else {
if (open != null) {
open.classList.toggle("active");
}
this.classList.toggle("active");
open = this;
//Scroll to clicked element
open.scrollIntoView();
scrollElmVert(open, -68);
}
});
}
</script>
</body>
</html>

not open second menu of accordion menu

I am using this accordion menu.
JavaScript:
var acc = document.getElementsByClassName("accordion"), i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
Html:
<!DOCTYPE html>
<html>
<head>
<style>
button.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
button.accordion.active, button.accordion:hover {
background-color: #ddd;
}
button.accordion:after {
content: '\02795';
font-size: 13px;
color: #777;
float: right;
margin-left: 5px;
}
button.accordion.active:after {
content: "\2796";
}
div.panel {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: 0.6s ease-in-out;
opacity: 0;
}
div.panel.show {
opacity: 1;
max-height: 500px;
}
</style>
</head>
<body>
<h2>Accordion with symbols</h2>
<p>In this example we have added a "plus" sign to each button. When the user clicks on the button, the "plus" sign is replaced with a "minus" sign.</p>
<button class="accordion">Section 3</button>
<div id="foo" class="panel">
<p>
<button class="accordion">Section 3</button>
<div id="foo" class="panel">
<p></p>
</div>
</p>
</div>
</body>
</html>
I need to use the second menu in section 3. I add in this accordion menu to another accordion menu but it does not open. Any ideas why? How can I solve this problem?
In the following HTML structure, I'm replacing P tag with DIV tag. The P element cannot contain block level elements like DIV.
Ref: How can I put DIV in P? and P tag
<body>
<h2>Accordion with symbols</h2>
<p>In this example we have added a "plus" sign to each button. When the user clicks on the button, the "plus" sign is replaced with a "minus" sign.</p>
<button class="accordion">Section 3</button>
<div id="foo" class="panel">
<div>
<button class="accordion">Section 3</button>
<div id="foo" class="panel">
<div>
test
</div>
</div>
</div>
</div>
</body>
The click event didn't work for your original HTML structure, as the browser altered the DOM elements and DIV element was removed from inside of P tag(explained the reason with the above reference links). This DOM change has resulted in an issue while we retrieve nextElementSibling.
Accept this answer if it solves your issue.

Categories

Resources