Expand/hide collapsible using javascript - 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!

Related

Collapsible accordion spreads and collapse instantly

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";
}
});
}

Vanilla JavaScript slideToggle to next item

I want to make dropdown item using vanilla js that has the same jQuery functionality with the next method. Because same class name not working with vanilla js
var button = document.querySelector('.ms-dropdown');
button.addEventListener('click', function(event) {
var next = this.nextElementSibling;
if (next.style.display == "none") {
next.style.display = "block";
} else {
next.style.display = "none";
}
});
<div class="ms-dropdown p-3">
<div class="row no-gutters align-items-center">
<div class="col-auto"><img src="images/tr-flag.png" alt="" /></div>
<div class="col-auto ml-2">Budesliga <span>(16)</span></div>
</div>
</div>
<div style="display:none">
asdfasdf
</div>
<div class="ms-dropdown p-3">
<div class="row no-gutters align-items-center">
<div class="col-auto"><img src="images/tr-flag.png" alt="" /></div>
<div class="col-auto ml-2">Budesliga <span>(16)</span></div>
</div>
</div>
<div style="display:none">
asdfasdf
</div>
MayBe Or Maybe not What you looking for
var button = document.querySelectorAll('.ms-dropdown');
for(let i = 0 ; i < button.length;i++){
button[i].addEventListener('click', function(event) {
var next = this.nextElementSibling;
if (next.style.display == "none") {
next.style.display = "block";
} else {
next.style.display = "none";
}
})
};
Please try this
document.querySelectorAll('.ms-dropdown').forEach(function(item,i){
item.addEventListener('click',function(){
var next = this.nextElementSibling;
next.style.display = next.style.display == 'none' ? 'block' : 'none';
});
});

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;
}

Showing and hiding div tags by their ID order using a button

I made a small questionnaire and I placed each question in a div tag. I'm trying to display the div tags in order, so when the user click the "Next" button it will move on to the next div and hide the previous one. Kind of like a paging system? I thought about creating some sort of a for loop but I feel like my code is super messy. Any suggestions? Thanks in advance.
<form>
<div id="1"></div>
<div id="2"></div>
<div id="3"></div>
<div id="4"></div>
<div id="5"></div>
<div id="6"></div>
<div id="7"></div>
<div id="8"></div>
<div id="9"></div>
<div id="10"></div>
<button type="button">Next</button>
<button type="button">Back</button>
</form>
<script>
visible(){
document.getElementById('1').style.display = "block";
document.getElementById('2').style.display = "none";
document.getElementById('3').style.display = "none";
document.getElementById('4').style.display = "none";
document.getElementById('5').style.display = "none";
document.getElementById('6').style.display = "none";
document.getElementById('7').style.display = "none";
document.getElementById('8').style.display = "none";
document.getElementById('9').style.display = "none";
document.getElementById('10').style.display = "none";
}
You can use javascript functions to setup your divs. This uses jQuery for smooth transition as well.
<div id="1">Question 1</div>
<div id="2" class="hidden">Question 2</div>
<div id="3" class="hidden">Question 3</div>
<div id="4" class="hidden">Question 4</div>
<div id="5" class="hidden">Question 5</div>
<div id="6" class="hidden">Question 6</div>
<div id="7" class="hidden">Question 7</div>
<div id="8" class="hidden">Question 8</div>
<div id="9" class="hidden">Question 9</div>
<div id="10" class="hidden">Question 10</div>
<button type="button" onclick="reverseDiv();">Back</button>
<button type="button" onclick="advanceDiv();">Next</button>
<script>
var divNum=1;
advanceDiv = function() {
if(divNum < 10) {
divNum++;
$('#' + (divNum-1)).slideToggle();
$('#' + divNum).slideToggle();
}
else {
/* last slide reached */
alert('last question');
}
}
reverseDiv = function() {
if(divNum > 1) {
divNum--;
$('#' + (divNum+1)).slideToggle();
$('#' + divNum).slideToggle();
}
else {
alert('first question');
}
}
</script>
Here's a jsfiddle to show functionality (and also includes arrow keys to move between functions): https://jsfiddle.net/79xupebb/1/
To think about the start and the end in this is case is important.
What happens if you are at the end and then click the next button? Then you have to go to the start. And if you are at the start point and click the previous button, you have to go to the end:
if(current === start && direction === "prev") current = end;
if(current === end && direction === "next") current = start;
To make all the divs display: none and only the first one display: block use CSS, like:
#container div {
display: none;
}
#container div:first-child {
display: block;
}
with HTML
<div id="container">
<div id="1">div1</div>
<div id="2">div2</div>
<div id="3">div3</div>
...
</div>
All togehter with plain JavaScript:
var prev = document.getElementById("prev");
var next = document.getElementById("next");
var start = 1;
var end = 10;
var current = 1;
function slide(direction, start, end) {
document.getElementById(current).style.display = "none";
if(current === start && direction === "prev") {
current = end;
} else if(current === end && direction === "next") {
current = start;
} else if(direction === "next") {
current += 1;
} else if(direction === "prev") {
current -= 1;
}
document.getElementById(current).style.display = "block";
}
prev.addEventListener("click", function() {
slide("prev", start, end);
});
next.addEventListener("click", function() {
slide("next", start, end);
});
#container div {
display: none;
}
#container div:first-child {
display: block;
}
<div id="container">
<div id="1">div1</div>
<div id="2">div2</div>
<div id="3">div3</div>
<div id="4">div4</div>
<div id="5">div5</div>
<div id="6">div6</div>
<div id="7">div7</div>
<div id="8">div8</div>
<div id="9">div9</div>
<div id="10">div10</div>
<button id="prev" type="button">Previous</button>
<button id="next" type="button">Next</button>
</div>
There is a simple way to do what you want. Use a counter (current), start it at 1 and increase it/ decrease it whenever the user clicks one of the buttons, then loop through your div list and make them all display:none except the one that corresponds to your current value.
var current = 1;
function next(){
current+=1;
for(var i = 1; i<10; i++){
document.getElementById(i).style.display = ((current==i)?"block":"none");
}
}
function back(){
current-=1;
for(var i = 1; i<=10; i++){
document.getElementById(i).style.display = ((current==i)?"block":"none");
}
}
<form>
<div id="1" style="display:block">one</div>
<div id="2" style="display:none">two</div>
<div id="3" style="display:none">three</div>
<div id="4" style="display:none">four</div>
<div id="5" style="display:none">five</div>
<div id="6" style="display:none">six</div>
<div id="7" style="display:none">aaaa</div>
<div id="8" style="display:none">bbb</div>
<div id="9" style="display:none">nnn</div>
<div id="10" style="display:none">ggg</div>
<button type="button" onclick="next()">Next</button>
<button type="button" onclick="back()">Back</button>
</form>
Note: My code has no safeguards so it will go below 1 and above 10, you can always check with an if statement in each of the buttons' functions.
var i;
for(i=2;i<=10;i++)
document.getElementById(''+i).style.display = "none";
i=1;
document.getElementById('btnNext').onclick = function(){
document.getElementById(''+i).style.display = "none";
i++;
document.getElementById(''+i).style.display = "block";
}
document.getElementById('btnPrev').onclick = function(){
document.getElementById(''+i).style.display = "none";
i--;
document.getElementById(''+i).style.display = "block";
}
<form>
<div id="1">1</div>
<div id="2">2</div>
<div id="3">3</div>
<div id="4">4</div>
<div id="5">5</div>
<div id="6">6</div>
<div id="7">7</div>
<div id="8">8</div>
<div id="9">9</div>
<div id="10">10</div>
<button type="button" id="btnNext">Next</button>
<button type="button" id="btnPrev">Back</button>
</form>
Add the onclick property to your buttons first:
<button type="button" onclick="showNext()">Next</button>
<button type="button" onclick="showPrev()">Back</button>
Then retrieve the divs by adding the class="quest" to every div
var el = document.getElementsByClassName("quest");
function showNext(){
for(var i=0;i<el.length; i++){
if (el[i].style.display == "block"){
el[i].style.display = "none";
if ((i+1)==el.length) i=-1;
el[i+1].style.display = "block";
break;
}
}
}
function showPrev(){
for(var i=0;i<el.length; i++){
if (el[i].style.display == "block"){
el[i].style.display = "none";
if (i==0) i=el.length;
el[i-1].style.display = "block";
break;
}
}
}

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