Collapsible accordion spreads and collapse instantly - javascript

I have a collapsible accordion placed inside a form. Pressing the button to spread, the accordion spreads and collapse back instantly. If I don't put it in the form, it works fine for me. This is the simplified code:
index.html:
<html>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<form method="post" id="platos">
<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>
</form>
<script src="collapsible.js"></script>
</body>
</html>
collapsible.js
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}

For each button inside the form tag, specify type="button", and these buttons will stop working as a submit:
<button type="button" class="accordion">Section</button>

Your form getting an action submit from the buttons.
you need to add to your function e.preventDefault() like this
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function (e) {
e.preventDefault()
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}

Related

On click show next tab in formular (div)

I got multiple divs with the class tab <div class="tab">i am div 1</div><div class="tab">i am div 2 </div>. Inside i have some input field anon the bottom I got a next and back button
<div class="funnel-buttons text-right">
<button type="button" class="icon-btn" id="prevBtn">Back</button>
<button type="button" class="icon-btn" id="nextBtn" onclick="nextTab()">Next</button>
</div>
now after clicking next or back button I want the next or prev div to shown up so I made a JS function when tab[0] is displayed and clicking next I should dissappear.
const tab = document.getElementsByClassName('tab');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
tab[0].style.display = 'block';
tab[1].style.display = 'none';
function nextTab () {
if(tab[0].style.display == 'block') {
tab[1].style.display = 'block';
tab[0].style.display = 'none';
}
}
But when I do so all the tabs disappear and nothing is shown.
Where is the error?
you can solve like this
const tab = document.getElementsByClassName('tab');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
function nextTab() {
const currentTab = document.querySelector('.show');
const tabArray = Array.from(tab);
const currentIndex = tabArray.indexOf(currentTab);
console.log(currentIndex);
currentTab.classList.remove('show');
currentTab.classList.add('hide');
if (tabArray.length > currentIndex + 1) {
tabArray[currentIndex + 1].classList.remove('hide');
tabArray[currentIndex + 1].classList.add('show');
} else {
// to return first tab at the end
tabArray[0].classList.remove('hide');
tabArray[0].classList.add('show');
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.show {
display: block;
}
.hide {
display: none;
}
</style>
</head>
<body>
<div class="tab show">1</div>
<div class="tab hide">2</div>
<div class="tab hide">3</div>
<div class="funnel-buttons text-right">
<button type="button" class="icon-btn" id="prevBtn">Back</button>
<button type="button" class="icon-btn" id="nextBtn"
onclick="nextTab()">Next</button>
</div>
<script src="app.js"></script>
</body>
</html>
You can introduce a variable to capture which is the current tab - in my code currentVisible. Based on that you can manipulate which tab you want to see on the UI. Also you can use .forEach() to iterate through all the <div> elements which has tab class added. Finally you can change the visibility with ternary operator like i === currentVisible ? 'block' : 'none'.
Try as the following:
const tab = document.getElementsByClassName('tab');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
let currentVisible = 0;
const handleTab = n => {
if (currentVisible + n >= 0 && currentVisible + n < tab.length) {
currentVisible += n;
Array.prototype.forEach.call(tab, (e, i) => {
tab[i].style.display = i === currentVisible ? 'block' : 'none';
});
}
}
handleTab(0);
<div class="funnel-buttons text-right">
<button type="button" class="icon-btn" id="prevBtn" onclick="handleTab(-1)">Back</button>
<button type="button" class="icon-btn" id="nextBtn" onclick="handleTab(1)">Next</button>
</div>
<div class="tab">I am div 1</div>
<div class="tab">I am div 2 </div>
<div class="tab">I am div 3</div>
<div class="tab">I am div 4 </div>
I hope this helps!

Expand/hide collapsible using javascript

I want to implement expandable menu. Only one heading should be expanded. When user clicks on another one, content of the previously
expanded heading should hide. I have used code from w3schools, but I don't know how to automatically hide previous heading.
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";
}
});
}
<h2>Collapsibles</h2>
<p>A Collapsible:</p>
<button class="collapsible">Open Collapsible</button>
<div class="content">
<p>text</p>
</div>
<p>Collapsible Set:</p>
<button class="collapsible">Open Section 1</button>
<div class="content">
<p>text</p>
</div>
<button class="collapsible">Open Section 2</button>
<div class="content">
<p>text.</p>
</div>
<button class="collapsible">Open Section 3</button>
<div class="content">
<p>text</p>
</div>
Simply collapse opened collapsible on click by JS:
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
var content = this.nextElementSibling;
if (this.classList.contains("active")) {
content.style.opacity = 0;
} else {
if(node=document.querySelector(".collapsible.active")){
node.classList.toggle("active",false);
node.nextElementSibling.style.opacity = 0;
}
content.style.opacity = 1;
}
this.classList.toggle("active");
});
}
.content{
opacity:0;
transition:opacity 0.5s;
}
<h2>Collapsibles</h2>
<p>A Collapsible:</p>
<button class="collapsible">Open Collapsible</button>
<div class="content">
<p>text</p>
</div>
<p>Collapsible Set:</p>
<button class="collapsible">Open Section 1</button>
<div class="content">
<p>text1</p>
</div>
<button class="collapsible">Open Section 2</button>
<div class="content">
<p>text2</p>
</div>
<button class="collapsible">Open Section 3</button>
<div class="content">
<p>text3</p>
</div>
Or, to use with height, you'll need to add overflow-y:hidden to hide it completely:
var coll = document.getElementsByClassName("collapsible");
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
console.trace()
debugger
var content = this.nextElementSibling;
if (this.classList.contains("active")) {
content.style.height = 0;
} else {
if(node=document.querySelector(".collapsible.active")){
node.classList.toggle("active",false);
node.nextElementSibling.style.height = 0;
}
content.style.height = content.scrollHeight+"px";
}
this.classList.toggle("active");
});
}
.content{
height:0;
transition:height 0.5s;
overflow-y:hidden;
}
<h2>Collapsibles</h2>
<p>A Collapsible:</p>
<button class="collapsible">Open Collapsible</button>
<div class="content">
<p>text1</p>
</div>
<p>Collapsible Set:</p>
<button class="collapsible">Open Section 1</button>
<div class="content">
<p>text1</p>
</div>
<button class="collapsible">Open Section 2</button>
<div class="content">
<p>text2</p>
</div>
<button class="collapsible">Open Section 3</button>
<div class="content">
<p>text3</p>
</div>
A more readable way to do this is to divide your code in functions, like this:
<script>
function hideOthers(actual) {
var contentDivs = document.querySelectorAll('.content');
contentDivs.forEach(others => {
if (others !== actual) {
others.style.display = 'none';
}
});
}
function toggleDisplay(div) {
if (div.style.display === "block") {
div.style.display = "none";
} else {
div.style.display = "block";
}
}
function onContentLoaded() {
var collapsibleDivs = document.querySelectorAll(".collapsible");
collapsibleDivs.forEach(div => {
div.addEventListener('click', e => {
var clicked = e.srcElement;
var sibling = clicked.nextElementSibling;
toggleDisplay(sibling);
hideOthers(sibling);
})
})
}
document.addEventListener("DOMContentLoaded", onContentLoaded);
</script>
Notice I've just changed your JS script and I've used document.querySelectorAll to get all elements with a given class, forEach function to iterate over elements, arrow functions to provide callbacks and e.srcElement to get the clicked element on the click handler. Hope it help you. Welcome to SO!

Javascript button onclick doesn't work on first click

I know similar questions have been asked before but I'm new to Javascript and none of the answers could help me solve my problem.
What I'm trying to do:
four buttons that open modals
but like the title already says, is there no reaction until the buttons have been clicked twice.
Here's my html:
<a id="myBtn" class="btn" href="javascript:void();" onclick="termsFct(this)">View full terms</a>
<div id="myModal" class="modal">
<div class="modal-content">
<span id="x" class="close">×</span>
<h1 class="contract">sample heading</h1>
<h2 class="contract">sample heading</h2>
<p class="fullContract">sample text</p>
</div>
</div>
</td>
<a id="myBtn2" class="btn" href="javascript:void();" onclick="termsFct(this)">View full terms</a>
<div id="myModal2" class="modal">
<div class="modal-content">
<span id="x2" class="close">×</span>
<h1 class="contract">sample heading</h1>
<h2 class="sample heading</h2>
<p class="fullContract">sample text</p>
</div>
</div>
</td>
<a id="myBtn3" class="btn" href="javascript:void();" onclick="termsFct(this)">View full terms</a>
<div id="myModal3" class="modal">
<div class="modal-content">
<span id="x3" class="close">×</span>
<h1 class="contract">sample heading</h1>
<h2 class="contract">sample heading</h2>
<p class="fullContract">sample text</p>
</div>
</div>
</td>
<a id="myBtn4" class="btn" href="javascript:void();" onclick="termsFct(this)">View full terms</a>
<div id="myModal4" class="modal">
<div class="modal-content">
<span id="x4" class="close">×</span>
<h1 class="contract">sample heading</h1>
<h2 class="contract">sample heading</h2>
<p class="fullContract">sample text</p>
</div>
</div>
</td>
and here's the Javascript:
/*------------------------modal------------------------*/
function termsFct(buttonElement) {
var modal = document.getElementById('myModal');
var modal2 = document.getElementById('myModal2');
var modal3 = document.getElementById('myModal3');
var modal4 = document.getElementById('myModal4');
var btn = document.getElementById("myBtn");
var btn2 = document.getElementById("myBtn2");
var btn3 = document.getElementById("myBtn3");
var btn4 = document.getElementById("myBtn4");
var span = document.getElementById("x");
var span2 = document.getElementById("x2");
var span3 = document.getElementById("x3");
var span4 = document.getElementById("x4");
/*------------------------contract visibility------------------------*/
btn.onclick = function() {
modal.style.display = "block";
}
btn2.onclick = function() {
modal2.style.display = "block";
}
btn3.onclick = function() {
modal3.style.display = "block";
}
btn4.onclick = function() {
modal4.style.display = "block";
}
/*------------------------close if x was clicked------------------------*/
span.onclick = function() {
modal.style.display = "none";
}
span2.onclick = function() {
modal2.style.display = "none";
}
span3.onclick = function() {
modal3.style.display = "none";
}
span4.onclick = function() {
modal4.style.display = "none";
}
/*------------------------close if clicked outside------------------------*/
window.onclick = function(event) {
if (event.target == modal) {
modal.scrollTop = 0;
modal.style.display = "none";
}
else if (event.target == modal2) {
modal2.scrollTop = 0;
modal2.style.display = "none";
}
else if (event.target == modal3) {
modal3.scrollTop = 0;
modal3.style.display = "none";
}
else if (event.target == modal4) {
modal4.scrollTop = 0;
modal4.style.display = "none";
}
}
}
I would really appreciate it if someone could help me solve this problem and even better help me shorten the Javascript.
thanks in advance
Ken
The termsFct method is already fired when the button is clicked. so no need of an onclick inside this method.
Replace the btn.onclick section with the below code and add more cases.
Switch(buttonelement.id){
Case btn1:
Modal.style.display="block";
Break;
}

How to make a JavaScript to JQuery conversion

I need a bit of help converting javascript to jquery. I'm using an accordion for a website i
I'm creating.
var acc = $(".accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
Here's the HTML:
<div id="our-club-accordion">
<button class="accordion">Our Goal</button>
<div class="panel">
</div>
<button class="accordion">Our Mission</button>
<div class="panel">
</div>
</div>
Seems it is that you need:
$('.accordion').on('click', function() {
$(this).toggleClass('active').next().toggleClass('show');
});

JQuery run a function when a div shows

I want to run a function when a div shows.
HTML:
<div id="holder">
<div id="main1" class="container-fluid">
<h1>Header 1</h1>
<div class="btn btn-default pull-right" onclick="show('main2');">Next</div>
</div>
<div id="main2" class="container-fluid" style="display:none;">
<h1>Header 2</h1>
<div class="btn btn-default pull-right" onclick="show('main3');">Next</div>
</div>
<div id="main3" class="container-fluid" style="display:none;">
<h1>Header 3</h1>
</div>
</div>
JS:
function show(elementID) {
var ele = document.getElementById(elementID);
if (!ele) {
alert("no such element");
return;
}
var pages = document.getElementsByClassName('container-fluid');
for (var i = 0; i < pages.length; i++) {
pages[i].style.display = 'none';
}
ele.style.display = 'block';
}
I want to run a function when my #main3 shows. The function is to set a timer so that after showing #main3 for 10 sec, it will show #main1.
Now how do I detect when my #main3 shows?
You can check for elementID, if it is main3, then set setTimeout for 10000 ms (10s) and call show('main1') in timeout after the line ele.style.display = 'block'; as following:
function show(elementID) {
var ele = document.getElementById(elementID);
if (!ele) {
alert("no such element");
return;
}
var pages = document.getElementsByClassName('container-fluid');
for(var i = 0; i < pages.length; i++) {
pages[i].style.display = 'none';
}
ele.style.display = 'block';
if(elementID == "main3"){
setTimeout(function(){ show("main1"); }, 10000);
}
}
DEMO
Since you have used jQuery tag, you can use a jQuery solution in which we can see whether the current tab is the last one if so then use a timer to show the first one like
jQuery(function($) {
$('#holder .nxt').click(function() {
var $parent = $(this).parent();
var $next = $parent.hide().next().show();
if ($next.is(':last-child')) {
setTimeout(function() {
$next.hide().siblings().first().show();
}, 2500)
}
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="holder">
<div id="main1" class="container-fluid">
<h1>Header 1</h1>
<div class="btn btn-default pull-right nxt">Next</div>
</div>
<div id="main2" class="container-fluid" style="display:none;">
<h1>Header 2</h1>
<div class="btn btn-default pull-right nxt">Next</div>
</div>
<div id="main3" class="container-fluid" style="display:none;">
<h1>Header 3</h1>
</div>
</div>
after this:
ele.style.display = 'block';
you can have a check like:
ele.style.display = 'block';
if(ele.id === "main3"){
// call it here.
}
function show(elementID) {
var ele = document.getElementById(elementID);
if (!ele) {
alert("no such element");
return;
}
var pages = document.getElementsByClassName('container-fluid');
for(var i = 0; i < pages.length; i++) {
pages[i].style.display = 'none';
}
ele.style.display = 'block';
if(elementID=='main3'){
yourFunction();// call your function here!
}
}

Categories