Search code examples
javascriptbootstrap-4menuexpandexpandable

expand menu to expand/collapse all menues regardless if they are expanded or closed


I have 4 expandable menus and 5 buttons. Collapse first button expands/closes the first menu, Collapse second button expands/closes the second menu and so on.

As it is: Button Collapse all for each menu closes it if it is expanded and expands it if it is closed.

Want to be: Whereas I want my Collapse all button to expand all menus if they are closed and don't change them if they are expanded. And then if clicked again closes all the menus that are expanded and don't change the closed menus. Here is a fiddle I wrote for As it is: expand menu fidlle

.row {
  background: #f8f9fa;
  margin-top: 20px;
}

.col {
  border: solid 1px #6c757d;
  padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.css" rel="stylesheet"/>

<p>
  <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseExample1, #collapseExample2, #collapseExample3, #collapseExample4" aria-expanded="false" aria-controls="collapseExample">
    Collapse all
  </button>
   <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseExample1" aria-expanded="false" aria-controls="collapseExample">
    Collapse first
  </button>
  <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseExample2" aria-expanded="false" aria-controls="collapseExample">
    Collapse second
  </button>
  <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseExample3" aria-expanded="false" aria-controls="collapseExample">
    Collapse third
  </button>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseExample4" aria-expanded="false" aria-controls="collapseExample">
    Collapse fourth
  </button>
</p>
<div class="collapse" id="collapseExample1">
  <div class="card card-body">
    hi first
  </div>
</div>
<div class="collapse" id="collapseExample2">
  <div class="card card-body">
    hi second
  </div>
</div>
<div class="collapse" id="collapseExample3">
  <div class="card card-body">
    hi third
  </div>
</div>
<div class="collapse" id="collapseExample4">
  <div class="card card-body">
    hi fourth
  </div>
</div>

How can I do it?


Solution

  • A naive solution I can think of now is to bind the collapse all click event via a custom handler that shows or hides all collapsibles based on a state on the collapse all button, instead of via Bootstrap built-in data-toggle="collapse" and data-target="*".

    <p>
        <button id="toggle-all" class="btn btn-primary" type="button">
            Collapse all
        </button>
        <button class="btn btn-primary" type="button" data-toggle="collapse" ... />
        ...
    </p>
    <div class="collapse" />
    ...
    

    Then in this Collapse all's button click event, you can have a state on this button itself (via data-) to memorize whether to show or hide all collapsibles. And based on that state variable, you can call either .collapse('show') or .collapse('hide') on all collapsibles manually.

    The show method won't show a collapsible again if it's already shown. And the hide method won't hide a collapsible if it's already hidden.

    $(function() {
        $('#toggle-all').click(function() {
            // Declare a variable, `data-to-show`, to contain the state whether to show/hide
            // all collapsibles.
            // It defaults to true, which means it's about to show all.
            if (!$(this).data('to-show')) {
                $(this).data('to-show', true);
            } else {
                $(this).data('to-show', !$(this).data('to-show'));
            }
            
            if ($(this).data('to-show')) {
                $('.collapse').collapse('show');
            } else {
                $('.collapse').collapse('hide');
            }
            return false;
        });
    });
    

    enter image description here


    demo: https://jsfiddle.net/davidliang2008/cvy2kbpz/28/


    Another better "solution" I can think of is to declare an extra data attribute, maybe something like data-toggle-all-at-the-same-time="true", and override Bootstrap's built-in .collapse() so that when it sees that data attribute being true, it doesn't toggle the target elements which are already toggled?

    Sadly I haven't found an easy way to do so, yet.