How to save the collapse state on reload Bootstrap 5 - javascript

I am new at bootstrap and javascript. How can I save the collapsed state so that the state retains on-page refresh?
<p>
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample">
Collapse Button
</button>
</p>
<div class="collapse" id="collapseExample">
<div class="card card-body">
Makers Academy engineering project has commenced. We have had a great experience so far with rails and now feel like we are getting to grips with the more complicated aspects. This guide is a quick introduction into adding likes to posts.
</div>
</div>
And my Javascript attempt which isn't working.
$(document).ready(function () {
$(".collapse").on("shown.bs.collapse", function () {
localStorage.setItem("coll_" + this.id, true);
});
$(".collapse").on("hidden.bs.collapse", function () {
localStorage.removeItem("coll_" + this.id);
});
$(".collapse").each(function () {
if (localStorage.getItem("coll_" + this.id) === "true") {
$(this).collapse("show");
}
else {
$(this).collapse("hide");
}
});
});

It does work - see this fiddle.
I see you're using $ notation - so you'll need jQuery loaded too. Make sure it's loaded before bootstrap.js.

Related

How do you get a javascript function to recognize html code returned by an ajax request

I apologize ahead of time if this question has been addressed...I looked and did not see one. I am building a web app using flask. After successfully logging in the user is routed to index.html via the following code:
#blueprint.route('/index')
#login_required
def index():
return render_template('home/index.html', segment='index')
My index.html includes an ajax request which builds and returns custom html such as:
<div class="card" style="max-width: 22rem;">
<!-- Card content -->
<div class="card-body pb-3">
<!-- Title -->
<h4 class="card-title font-weight-bold">TailNumber</h4>
<h6 class="card-subtitle text-muted d-flex justify-content-between">
<p>AircraftType</p>
<p>Operator</p>
</h6>
<div class="collapse-content">
<div class="collapse" id="tail_number" style="">
<table class="table table-borderless table-sm mb-0">
<tbody>
<tr>
<td class="font-weight-normal align-middle">Engine</td>
<td class="float-right font-weight-normal">
<p class="mb-1">2.5<span class="text-muted">IPS</span></p>
</td>
</tr>
</tbody>
</table>
</div>
<hr class="">
<button class="btn" type="button" data-toggle="collapse" data-target="#tail_number"
aria-expanded="false" aria-pressed="false" aria-controls="tail_number">Show
</button>
</div>
</div>
</div>
I also include a javascript function designed to change the text of my "collapse" button from "Show" to "Hide" and back again as the content is expanded and collapsed:
$(document).ready(function() {
$('[data-toggle="collapse"]').click(function() {
$(this).toggleClass("active");
if ($(this).hasClass("active")) {
$(this).text("Hide");
} else {
$(this).text("Show");
}
});
});
While the collapse feature of my html code works the text is not being updated. If I hard code the html instead of loading it via my ajax request then the button text changes as advertised. My assumption is that the javascript above is not being applied to the html returned by the ajax request but I am not sure how to verify this or, more importantly, correct it. My simplified ajax request is here:
<script>
$(function(){
function update_fleet_view(){
// create an ajax request
var req = new XMLHttpRequest();
req.open("GET", "/ajax", true);
req.send();
req.onload = function () {
// update page with new fleet view
var data = JSON.parse(req.responseText);
document.getElementById("fleet_view").innerHTML = data["fleet_view"];
// timeout to run again after 1 minute
setTimeout(update_fleet_view, 60000);
}
}
// call the function to get it started
update_fleet_view();
})();
</script>
Event delegation was exactly what I needed...thanks for the link James.
I ended up with the following modifications to my JS:
$(document).ready(function() {
$("#fleet_view").on("click", '[data-toggle="collapse"]', function() {
if ($(this).hasClass("collapsed")) {
$(this).text("Hide");
} else {
$(this).text("Show");
}
});
});
where "fleet_view" is the "id" that is referenced by my ajax script.
I attempted to leave the $(this).toggleClass("active") in the script however I could never get it to toggle. Rather than spending precious time tracking that issue down I decided to look at the "collapsed" field as the click event toggles that automatically. I also had to modify my button to add the "collapsed" class otherwise the button text would not update for the first round of clicks (see code below). The "if/else" statement seems backwards but I assure you it works as intended. Again, I can only assume that the "collapsed" class gets added or removed after this script executes.
<button class="btn collapsed" type="button" data-toggle="collapse" data-target="#tail_number"
aria-expanded="false" aria-pressed="false" aria-controls="tail_number">Show
</button>

Accordion .glyphicon-chevron-up and .glyphicon-chevron-down not working

I am using an Accordion with up and down arrows -when run first time down arrow is displayed so user had to click to see data - the data is shown on clicking the down arrow but the up arrow is still shown and doesn't revert to up...I have a Javascript function which does the checking...
Followed this link:-
https://www.bootply.com/113766#
Javascript to change the arrows:-
$(document).ready(function ()
{
$('.collapse').on('shown.bs.collapse', function () {
$(this).parent().find(
".glyphicon-chevron-up").removeClass("glyphicon-chevron-up")
.addClass("glyphicon-chevron-down");
}).on('hidden.bs.collapse', function ()
{
$(this).parent().find(".glyphicon-chevron-down")
.removeClass("glyphicon-chevron-down").addClass("glyphicon-chevron-
up");
});
});
HTML:-
<div class="accordion">
<h4 class="up">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
Testing
<span class="glyphicon glyphicon-chevron-up" style="float: right"></span>
</a>
</h4>
<div id="collapseOne" class="collapse">
The default arrow is always displayed - the data is displayed on click
Turns out it was pointing to the wrong JQuery version - all working now...

Expand/Collapse several blocks at the same time?

I have several blocks in my page. I use bootstrap 4 alpha 6 version. I want expand/collapse these blocks by clicking one button. Right know I use next js code and it only open all blocks but how to close them?! How to fix this problem?
HTML:
<div class="card">
<div class="card-header">
<button id='expand-collapse' type="button" data-parent="#blocks" data-toggle="collapse" data-target=".block" aria-expanded="false" aria-controls=".block">
</button>
</div>
<div class="card-block">
<div id="blocks">
<div class="list-group">
<div class="list-group-item">
<a data-toggle="collapse" href="#block-1" aria-expanded="false" aria-controls="block-1"OPEN FIRST</a>
<div class="collapse block" id="block-1">
<!--FIRST BLOCK BLOCK-->
</div>
</div>
<div class="list-group-item">
<a data-toggle="collapse" href="#block-2" aria-expanded="false" aria-controls="block-2">OPEN SECOND</a>
<div class="collapse block" id="block-2">
<!--SECOND BLOCK-->
</div>
</div>
<div class="list-group-item">
<a data-toggle="collapse" href="#block-3" aria-expanded="false" aria-controls="block-3">OPEN THIRD</a>
<div class="collapse block" id="block-3">
<!--THIRD BLOCK-->
</div>
</div>
</div>
</div>
</div>
</div>
JAVASCRIPT:
$(function() {
$('#expand-collapse').on('click', function() { // We capture the click event
var target = $(this).attr('data-target'); // We get teh target element selector
$(target).each(function() { // Loop through each element
if ($(this).hasClass('show')) { // Check if it's already visible or not
$(this).collapse('hide'); // Show and hide accordingly
} else {
$(this).collapse('show');
}
});
});
});
$(document).ready(function () {
$('.collapse')
.on('shown.bs.collapse', function(event) {
event.stopPropagation();
$(this)
.parent().parent()
.find(".fa-commenting-o")
.removeClass("fa-commenting-o")
.addClass("fa-commenting");
}).on('hidden.bs.collapse', function(event) {
event.stopPropagation();
$(this)
.parent().parent()
.find(".fa-commenting")
.removeClass("fa-commenting")
.addClass("fa-commenting-o");
});
});
The code that youve used will work as expected in bootstrap3 due to the way collapse was handled then (You can verify it by using JS & CSS of bootstrap V3)
Comming to solving your problem the following snippet would work as expected:
$(function() {
$('#expand-collapse').on('click', function() { // We capture the click event
var target = $(this).attr('data-target'); // We get teh target element selector
$(target).each(function() { // Loop through each element
if ($(this).hasClass('show')) { // Check if it's already visible or not
$(this).collapse('hide'); // Show and hide accordingly
} else {
$(this).collapse('show');
}
});
});
});
TIP:
We can also pass toggle argument to the collapse function and get rid of the if-else condition
$(this).collapse('toggle'); can be used to replace the if-else
But I did not use this in my example to show that you can add additional computation in it
Working Fiddle
UPDATE:
The updated question asks for individual control for the block
To acheive that, we can use the default method of triggering the action with a button element.
<div class="list-group-item">
<div class="collapse block" id="block-1">
FIRST BLOCK
</div>
</div>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#block-1">
Block 1
</button>
You can find the updated jsFidde here
Add an else statement to handle all other cases that don't match the criteria to show. Try this:
$(function () {
$('#expand-collapse').on('click',function(){
$('[data-toggle="collapse"]').each(function(){
var objectID=$(this).attr('data-target');
if($(objectID).hasClass('.collapse')===false)
{
$(objectID).collapse('show');
} else {
$(objectID).collapse('hide');
}
});
});
});
There is an error with the Bootstrap v4.0.0-alpha.6 version with the transitioning that will be solved on the next release.
See the issue 22256 and pull 21743 links for more information.

Toggle the glyphicon with bootstrap collapse

I have glyphicon-chevron-down when div is in collapsed state,i want to replace it with glyphicon-chevron-up when the div is in collapse in state.I have many of these collapsible divs in my project.So i need to toggle the glyphicon on press of particular collapsibe content(or div).
HTML
<div class="divcontainer">
<span>Click -----></span>
<span class="glyphicon glyphicon-chevron-down" data-toggle="collapse" href="#collapsecontent"></span>
</div>
<div class="collapse" id="collapsecontent">
content
</div>
JavaScript
$(document).ready(function () {
$('.glyphicon-chevron-down').click(function () {
$(this).parent("div").find(".glyphicon-chevron-down")
.toggleClass("glyphicon-chevron-up");
});
});
Fiddle here
Your JS is putting an emphasis on the wrong thing. Find is very slow and best avoided if possible -- and it's very possible here. Just change it to this:
$(document).ready(function () {
$('.glyphicon').click(function () {
$(this).toggleClass("glyphicon-chevron-down").toggleClass("glyphicon-chevron-up");
});
});
JS Fiddle: http://jsfiddle.net/ft4mnynh/
Try the FIDDLE,
I have changed the selectors and events like below
$(document).ready(function () {
$('.divcontainer').click(function () {
$(this).find("span:eq(1)")
.toggleClass('glyphicon-chevron-down')
.toggleClass("glyphicon-chevron-up");
});
});
Hope this helps
Use this below jquery code and just change glyphicon name as you wish
<script>
$('.collapse').on('shown.bs.collapse', function(){
$(this).parent().find(".glyphicon-menu-right").removeClass("glyphicon-menu-right").addClass("glyphicon-menu-down");
}).on('hidden.bs.collapse', function(){
$(this).parent().find(".glyphicon-menu-down").removeClass("glyphicon-menu-down").addClass("glyphicon-menu-right");
});
</script>
You need to toggle both classes chevron-up and chevron-down.
You can find the correct div using the more general glyphicon class.
I also added id to the div, so you don't need to find it by the down class
See: http://jsfiddle.net/amwLaojj/1/
$(document).ready(function () {
$('#myglyph').click(function () {
$(this).parent("div").find(".glyphicon")
.toggleClass("glyphicon-chevron-up")
.toggleClass("glyphicon-chevron-down");
});
});
Update:
Instead of ID, you can use:
$('.glyphicon[data-toggle=collapse]').click(function () {
Updated in: http://jsfiddle.net/amwLaojj/3/
The following code worked for me. As far as I can tell the bootstrap js events only fire on the "div.collapse" element, i.e., the hidden and shown block(s). Even the jquery "click" event on "div.panel-heading" or any child was unresponsive.
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
Getting Started
<span class="glyphicon glyphicon-menu-right" aria-hidden="true"></span></a></h4>
</div>
<div id="collapseOne" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
<a id="choosing">Choosing a topic</a>
<a id="exploring">Exploring a topic</a>
</div></div>
JQuery
$(document).ready(function () {
$('div.collapse').on("show.bs.collapse || hide.bs.collapse", function () {
let glyph = $(this).siblings("div.panel-heading").find('span.glyphicon');
glyph.hasClass("glyphicon-menu-right") ? glyph.toggleClass("glyphicon-menu-down") : glyph.toggleClass("glyphicon-menu-right");
});
});

Twitter bootstrap collapse: change display of toggle button

I am using Twitter Bootstrap to create collapsible sections of text. The sections are expanded when a + button is pressed. My html code as follows:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button type="button" class="btn btn-success" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
Is there a way to change the button to display - instead of + after the section is expanded (and change back to + when it is collapsed again)?
Additional information: I hoped there would be a simple twitter-bootstrap/css/html-based solution to my problem. All responses so far make use of JavaScript or PHP. Because of this I want to add some more information about my development environment: I want to use this solution inside a SilverStripe-based (version 3.0.5) website which has some implications for the use of both PHP as well as JavaScript.
try this. http://jsfiddle.net/fVpkm/
Html:-
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button class="btn btn-success" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
JS:-
$('button').click(function(){ //you can give id or class name here for $('button')
$(this).text(function(i,old){
return old=='+' ? '-' : '+';
});
});
Update With pure Css, pseudo elements
http://jsfiddle.net/r4Bdz/
Supported Browsers
button.btn.collapsed:before
{
content:'+' ;
display:block;
width:15px;
}
button.btn:before
{
content:'-' ;
display:block;
width:15px;
}
Update 2 With pure Javascript
http://jsfiddle.net/WteTy/
function handleClick()
{
this.value = (this.value == '+' ? '-' : '+');
}
document.getElementById('collapsible').onclick=handleClick;
Here's another CSS only solution that works with any HTML layout.
It works with any element you need to switch. Whatever your toggle layout is you just put it inside a couple of elements with the if-collapsed and if-not-collapsed classes inside the toggle element.
The only catch is that you have to make sure you put the desired initial state of the toggle. If it's initially closed, then put a collapsed class on the toggle.
It also requires the :not selector, so it doesn't work on IE8.
HTML example:
<a class="btn btn-primary collapsed" data-toggle="collapse" href="#collapseExample">
<!--You can put any valid html inside these!-->
<span class="if-collapsed">Open</span>
<span class="if-not-collapsed">Close</span>
</a>
<div class="collapse" id="collapseExample">
<div class="well">
...
</div>
</div>
Less version:
[data-toggle="collapse"] {
&.collapsed .if-not-collapsed {
display: none;
}
&:not(.collapsed) .if-collapsed {
display: none;
}
}
CSS version:
[data-toggle="collapse"].collapsed .if-not-collapsed {
display: none;
}
[data-toggle="collapse"]:not(.collapsed) .if-collapsed {
display: none;
}
JS Fiddle
Add some jquery code, you need jquery to do this :
<script>
$(".btn[data-toggle='collapse']").click(function() {
if ($(this).text() == '+') {
$(this).text('-');
} else {
$(this).text('+');
}
});
</script>
All the other solutions posted here cause the toggle to get out of sync if it is double clicked. The following solution uses the events provided by the Bootstrap framework, and the toggle always matches the state of the collapsible element:
HTML:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button id="intro-switch" class="btn btn-success" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
JS:
$('#intro').on('show', function() {
$('#intro-switch').html('-')
})
$('#intro').on('hide', function() {
$('#intro-switch').html('+')
})
That should work for most cases.
However, I also ran into an additional problem when trying to nest one collapsible element and its toggle switch inside another collapsible element. With the above code, when I click the nested toggle to hide the nested collapsible element, the toggle for the parent element also changes. It may be a bug in Bootstrap. I found a solution that seems to work: I added a "collapsed" class to the toggle switches (Bootstrap adds this when the collapsible element is hidden but they don't start out with it), then added that to the jQuery selector for the hide function:
http://jsfiddle.net/fVpkm/87/
HTML:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button id="intro-switch" class="btn btn-success collapsed" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...<br>
<a id="details-switch" class="collapsed" data-toggle="collapse" href="#details">Show details</a>
<div id="details" class="collapse">
More details...
</div>
</div>
</div>
JS:
$('#intro').on('show', function() {
$('#intro-switch').html('-')
})
$('#intro').on('hide', function() {
$('#intro-switch.collapsed').html('+')
})
$('#details').on('show', function() {
$('#details-switch').html('Hide details')
})
$('#details').on('hide', function() {
$('#details-switch.collapsed').html('Show details')
})
I liked the CSS-only solution from PSL, but in my case I needed to include some HTML in the button, and the content CSS property is showing the raw HTML with tags in this case.
In case that could help someone else, I've forked his fiddle to cover my use case: http://jsfiddle.net/brunoalla/99j11h40/2/
HTML:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button class="btn btn-success collapsed" data-toggle="collapse" data-target="#intro">
<span class="show-ctrl">
<i class="fa fa-chevron-down"></i> Expand
</span>
<span class="hide-ctrl">
<i class="fa fa-chevron-up"></i> Collapse
</span>
</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
CSS:
button.btn .show-ctrl{
display: none;
}
button.btn .hide-ctrl{
display: block;
}
button.btn.collapsed .show-ctrl{
display: block;
}
button.btn.collapsed .hide-ctrl{
display: none;
}
My following JS solution is better than the other approaches here because it ensures that it will always say 'open' when the target is closed, and vice versa.
HTML:
<a href="#collapseExample" class="btn btn-primary" data-toggle="collapse" data-toggle-secondary="Close">
Open
</a>
<div class="collapse" id="collapseExample">
<div class="well">
...
</div>
</div>
JS:
$('[data-toggle-secondary]').each(function() {
var $toggle = $(this);
var originalText = $toggle.text();
var secondaryText = $toggle.data('toggle-secondary');
var $target = $($toggle.attr('href'));
$target.on('show.bs.collapse hide.bs.collapse', function() {
if ($toggle.text() == originalText) {
$toggle.text(secondaryText);
} else {
$toggle.text(originalText);
}
});
});
Examples:
$('[data-toggle-secondary]').each(function() {
var $toggle = $(this);
var originalText = $toggle.text();
var secondaryText = $toggle.data('toggle-secondary');
var $target = $($toggle.attr('href'));
$target.on('show.bs.collapse hide.bs.collapse', function() {
if ($toggle.text() == originalText) {
$toggle.text(secondaryText);
} else {
$toggle.text(originalText);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet"/>
<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
<a href="#collapseExample" class="btn btn-primary" data-toggle="collapse" data-toggle-secondary="Close">
Open
</a>
<div class="collapse" id="collapseExample">
<div class="well">
...
</div>
</div>
JS Fiddle
Other benefits of this approach:
the code is DRY and reusable
each collapse button stays separate
you only need to put one change into the HTML: adding the data-toggle-secondary attribute
I guess you could look inside your downloaded code where exactly there is a + sign (but this might not be very easy).
What I'd do?
I'd find the class/id of the DOM elements that contain the + sign (suppose it's ".collapsible", and with Javascript (actually jQuery):
<script>
$(document).ready(function() {
var content=$(".collapsible").html().replace("+", "-");
$(".collapsible").html(content));
});
</script>
edit
Alright... Sorry I haven't looked at the bootstrap code... but I guess it works with something like slideToggle, or slideDown and slideUp... Imagine it's a slideToggle for the elements of class .collapsible, which reveal contents of some .info elements. Then:
$(".collapsible").click(function() {
var content=$(".collapsible").html();
if $(this).next().css("display") === "none") {
$(".collapsible").html(content.replace("+", "-"));
}
else $(".collapsible").html(content.replace("-", "+"));
});
This seems like the opposite thing to do, but since the actual animation runs in parallel, you will check css before animation, and that's why you need to check if it's visible (which will mean it will be hidden once the animation is complete) and then set the corresponding + or -.
Easier with inline coding
<button type="button" ng-click="showmore = (showmore !=null && showmore) ? false : true;" class="btn float-right" data-toggle="collapse" data-target="#moreoptions">
<span class="glyphicon" ng-class="showmore ? 'glyphicon-collapse-up': 'glyphicon-collapse-down'"></span>
{{ showmore !=null && showmore ? "Hide More Options" : "Show More Options" }}
</button>
<div id="moreoptions" class="collapse">Your Panel</div>
Some may take issue with changing the Bootstrap js (and perhaps validly so) but here is a two line approach to achieving this.
In bootstrap.js, look for the Collapse.prototype.show function and modify the this.$trigger call to add the html change as follows:
this.$trigger
.removeClass('collapsed')
.attr('aria-expanded', true)
.html('Collapse')
Likewise in the Collapse.prototype.hide function change it to
this.$trigger
.addClass('collapsed')
.attr('aria-expanded', false)
.html('Expand')
This will toggle the text between "Collapse" when everything is expanded and "Expand" when everything is collapsed.
Two lines. Done.
EDIT: longterm this won't work. bootstrap.js is part of a Nuget package so I don't think it was propogating my change to the server. As mentioned previously, not best practice anyway to edit bootstrap.js, so I implemented PSL's solution which worked great. Nonetheless, my solution will work locally if you need something quick just to try it out.
You do like this.
the function return the old text.
$('button').click(function(){
$(this).text(function(i,old){
return old=='Read More' ? 'Read Less' : 'Read More';
});
});
Applied and working in Bootstrap 5.0.1.
Using simple jQuery
jQuery('button').on( 'click', function(){
if(jQuery(this).hasClass('collapsed')){
jQuery(this).html('+');
} else {
jQuery(this).html('-');
}
});
You can also use font awesome or HTML instead of +/- signs.

Categories