cfx cfx - 7 months ago 10
Javascript Question

Bootstrap 3 collapse accordion: collapse all works but then cannot expand all while maintaining data-parent

I'm using Bootstrap 3 and trying to setup the following accordion/collapse structure:


  1. Onload: Each accordion panel in a group is fully collapsed and functions as documented/expected.

  2. Button click: Each accordion panel expands and clicking the toggles has no effect (including URL anchor effects).

  3. Another button click: All panels return to onload state; all collapsed and clickable as normal.



I've made it to step 2, but when I click the button again at step 3 it has no effect. I also see no console errors reported in Chrome Dev Tools or by running the code through my local JSHint.

I'd like this cycle to be repeatable each time the button is clicked.

I've setup my code here http://bootply.com/98140 and here http://jsfiddle.net/A9vCx/

I'd love to know what I'm doing wrong and I appreciate suggestions. Thank you!

My HTML:

<button class="collapse-init">Click to disable accordion behavior</button>
<br><br>
<div class="panel-group" id="accordion">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
Collapsible Group Item #1
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse">
<div class="panel-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo">
Collapsible Group Item #2
</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse">
<div class="panel-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseThree">
Collapsible Group Item #3
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse">
<div class="panel-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid.
</div>
</div>
</div>
</div>


My JS:

$(function() {

var $active = true;

$('.panel-title > a').click(function(e) {
e.preventDefault();
});

$('.collapse-init').on('click', function() {
if(!$active) {
$active = true;
$('.panel-title > a').attr('data-toggle', 'collapse');
$('.panel-collapse').collapse({'toggle': true, 'parent': '#accordion'});
$(this).html('Click to disable accordion behavior');
} else {
$active = false;
$('.panel-collapse').collapse({'toggle': true, 'parent': '#accordion'});
$('.panel-title > a').removeAttr('data-toggle');
$(this).html('Click to enable accordion behavior');
}
});

});

Answer

Updated Answer

Trying to open multiple panels of a collapse control that is setup as an accordion i.e. with the data-parent attribute set, can prove quite problematic and buggy (see this question on multiple panels open after programmatically opening a panel)

Instead, the best approach would be to:

  1. Allow each panel to toggle individually
  2. Then, enforce the accordion behavior manually where appropriate.

To allow each panel to toggle individually, on the data-toggle="collapse" element, set the data-target attribute to the .collapse panel ID selector (instead of setting the data-parent attribute to the parent control. You can read more about this in the question Modify Twitter Bootstrap collapse plugin to keep accordions open.

Roughly, each panel should look like this:

<div class="panel panel-default">
   <div class="panel-heading">
         <h4 class="panel-title"
             data-toggle="collapse" 
             data-target="#collapseOne">
             Collapsible Group Item #1
         </h4>
    </div>
    <div id="collapseOne" 
         class="panel-collapse collapse">
        <div class="panel-body"></div>
    </div>
</div>

To manually enforce the accordion behavior, you can create a handler for the collapse show event which occurs just before any panels are displayed. Use this to ensure any other open panels are closed before the selected one is shown (see this answer to multiple panels open). You'll also only want the code to execute when the panels are active. To do all that, add the following code:

$('#accordion').on('show.bs.collapse', function () {
    if (active) $('#accordion .in').collapse('hide');
});

Then use show and hide to toggle the visibility of each of the panels and data-toggle to enable and disable the controls.

$('#collapse-init').click(function () {
    if (active) {
        active = false;
        $('.panel-collapse').collapse('show');
        $('.panel-title').attr('data-toggle', '');
        $(this).text('Enable accordion behavior');
    } else {
        active = true;
        $('.panel-collapse').collapse('hide');
        $('.panel-title').attr('data-toggle', 'collapse');
        $(this).text('Disable accordion behavior');
    }
});

Working demo in jsFiddle