Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bootstrap Collapse complex behaviour

How do I implement Bootstrap 4 collapse on a hierarchy of elements (navbars in my case)

I have the following hierarchy on some elements for my page and I wish to use the collapse behavior in this “tree”.

Figure 1

Specifically I wish the following behavior

(which I believe is the one that makes sense)

Where green means the navbar is shown, and Red means it is collapsed

Figure 2

With the current state

and clicking on Option B1

Figure 3

this simply collapses the navbar B1

In another example when clicking on option A

Figure 4

This shows navbar A

and collapses all descendants of option B

(navbar B and navbar B1)

Figure 5

The rules can be brought down to 3 simple rules:

1) When clicking on an element with some children (and possibly other descendents) active (and here active I mean shown) all the children and descendants collapse.

2) When clicking on an element X that has no active children then show the (direct) children of X AND collapse all the descendants of the siblings of X.

3) all descendants of the primary nav are loaded collapsed

I could get the desired behaviour with custom javascript code and unfortunately I needed to ignore some desired functionalities already provided by the collapse plugin from bootstrap and reimplementing them according to my need.

Go here for the Bootply

Here is the simplified HTML part

<nav id="main-nav" class="navbar navbar-expand-lg navbar-dark">
    <ul class="navbar-nav align-nav">
        <li class="nav-item">
            <a id="togglenavA" class="nav-link" href="#" data-target="#navA" aria-controls="navA" aria-expanded="false" aria-label="Toggle navigation">Option A</a>
        </li>
        <li class="nav-item">
            <a id="togglenavB" class="nav-link" href="#" data-target="#navB" aria-controls="navB" aria-expanded="false" aria-label="Toggle navigation">Option B</a>
        </li>
    </ul>
</nav>
<div id="navA" class="collapse">
    <nav id="NAVA" class="navbar navbar-expand-lg navbar-dark">
        <ul class="navbar-nav align-nav">
            <li class="nav-item">
                <a class="nav-link" href="#">Option A1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Option A2</a>
            </li>
        </ul>
    </nav>
</div>
<div id="navB" class="collapse">
    <nav id="NAVB" class="navbar navbar-expand-lg navbar-dark">
        <ul class="navbar-nav align-nav">
            <li class="nav-item">
                <a id="togglenavB1" class="nav-link" href="#" data-target="#navB1" aria-controls="navB1" aria-expanded="false" aria-label="Toggle navigation">Option B1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Option B2</a>
            </li>
        </ul>
    </nav>
</div>
<div id="navB1" class="collapse">
    <nav id="NAVB1" class="navbar navbar-expand-lg navbar-dark">
        <ul class="navbar-nav align-nav">
            <li class="nav-item">
                <a class="nav-link" href="#">Option B1.1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Option B1.2</a>
            </li>
        </ul>
    </nav>
</div>

And here is the Javascript that reimplements the accordion style but with the additional collapse of the sibling's children

$(document).ready(function() {
    $('#togglenavA').click(function() {
        $('#navB').collapse('hide')
        $('#navB1').collapse('hide')
        $('#navA').collapse('toggle')
        $('#navA').on('hidden.bs.collapse', function () {
            $('#togglenavA').attr("aria-expanded","false");
        }) // for screen readers purpose
        $('#navA').on('shown.bs.collapse', function () {
            $('#togglenavA').attr("aria-expanded","true");
        })
    });
    $('#togglenavB').click(function() {
        $('#navA').collapse('hide')
        $('#navB1').collapse('hide')
        $('#navB').collapse('toggle')
        $('#navB').on('hidden.bs.collapse', function () {
            $('#togglenavB').attr("aria-expanded","false");
        })
        $('#navB').on('shown.bs.collapse', function () {
            $('#togglenavB').attr("aria-expanded","true");
        })
    });
    $('#togglenavB1').click(function() {
        $('#navB1').collapse('toggle')
        $('#navB1').on('hidden.bs.collapse', function () {
            $('#togglenavB1').attr("aria-expanded","false");
        })
        $('#navB1').on('shown.bs.collapse', function () {
            $('#togglenavB1').attr("aria-expanded","true");
        })
    });
});

Note: even though this solution solves my problem, it does so at the cost of ignoring Bootstrap's already implementation of an accordion and removing some data-attributes from some links to avoid conflicts between some automatic functionalities from the plugin with my custom code.

like image 810
gota Avatar asked Sep 06 '17 10:09

gota


People also ask

How does collapse work in bootstrap?

How it works. The collapse JavaScript plugin is used to show and hide content. Buttons or anchors are used as triggers that are mapped to specific elements you toggle. Collapsing an element will animate the height from it's current value to 0 .

How do I trigger a bootstrap collapse?

Just add data-toggle="collapse" and a data-target to the element to automatically assign control of one or more collapsible elements. The data-target attribute accepts a CSS selector to apply the collapse to. Be sure to add the class collapse to the collapsible element.

What is difference between collapse and accordion?

In bootstrap context wise, accordion is basically a collapse button with a lot of smaller info in it. Bootstrap use card to make an accordion. on line 1, <div id="accordion" role="tablist"> , this is where the data-parent refers to. on line 2 <div class="card"> , we are using a card class, to show the card effect.

How do I stop a bootstrap collapse?

Example Explained. The . collapse class indicates a collapsible element (a <div> in our example); this is the content that will be shown or hidden with a click of a button. To control (show/hide) the collapsible content, add the data-toggle="collapse" attribute to an <a> or a <button> element.


2 Answers

Working Bootply example

My approach involves the following updates:


1. Include missing attribute to your links (<a>)

Add the data-toggle="collapse" attribute to the menu links. This will give you the out-of-the-box expected Bootstrap behavior for collapse. Example:

<a id="togglenavA" class="nav-link" href="#" data-toggle="collapse" data-target="#navA" aria-controls="navA" aria-expanded="false" aria-label="Toggle navigation">Option A</a>

2. Add a secondary class collapse-child

This allows us to target these child navbars. Example:

<div id="navA" class="collapse collapse-child">

3. The JavaScript

The JavaScript can then be simplified to just the following snippet:

$(document).ready(function() {
  var hideChildren = function() {
    $('div.collapse-child').collapse('hide');
  };

  $('a.nav-link:not([aria-controls])').click(function(el){
    hideChildren();
  });

  $('#main-nav a').click(function(){
    hideChildren();
  });
});

I'm not sure if this fulfills your "without custom Javascript" requirement but I would consider it to be the "best-practice" way of achieving this with the most current version of Bootstrap4.

like image 183
inki Avatar answered Oct 01 '22 05:10

inki


Ok I found a way to do this with custom javascript code and ignoring some desired functionalities provided by the collapse plugin from bootstrap and reimplementing them according to my need.

Here is the simplified HTML part

<nav id="main-nav" class="navbar navbar-expand-lg navbar-dark">
    <ul class="navbar-nav align-nav">
        <li class="nav-item">
            <a id="togglenavA" class="nav-link" href="#" data-target="#navA" aria-controls="navA" aria-expanded="false" aria-label="Toggle navigation">Option A</a>
        </li>
        <li class="nav-item">
            <a id="togglenavB" class="nav-link" href="#" data-target="#navB" aria-controls="navB" aria-expanded="false" aria-label="Toggle navigation">Option B</a>
        </li>
    </ul>
</nav>
<div id="navA" class="collapse">
    <nav id="NAVA" class="navbar navbar-expand-lg navbar-dark">
        <ul class="navbar-nav align-nav">
            <li class="nav-item">
                <a class="nav-link" href="#">Option A1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Option A2</a>
            </li>
        </ul>
    </nav>
</div>
<div id="navB" class="collapse">
    <nav id="NAVB" class="navbar navbar-expand-lg navbar-dark">
        <ul class="navbar-nav align-nav">
            <li class="nav-item">
                <a id="togglenavB1" class="nav-link" href="#" data-target="#navB1" aria-controls="navB1" aria-expanded="false" aria-label="Toggle navigation">Option B1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Option B2</a>
            </li>
        </ul>
    </nav>
</div>
<div id="navB1" class="collapse">
    <nav id="NAVB1" class="navbar navbar-expand-lg navbar-dark">
        <ul class="navbar-nav align-nav">
            <li class="nav-item">
                <a class="nav-link" href="#">Option B1.1</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Option B1.2</a>
            </li>
        </ul>
    </nav>
</div>

And here is the Javascript that reimplements the accordion style but with the additional collapse of the sibling's children

$(document).ready(function() {
    $('#togglenavA').click(function() {
        $('#navB').collapse('hide')
        $('#navB1').collapse('hide')
        $('#navA').collapse('toggle')
        $('#navA').on('hidden.bs.collapse', function () {
            $('#togglenavA').attr("aria-expanded","false");
        }) // for screen readers purpose
        $('#navA').on('shown.bs.collapse', function () {
            $('#togglenavA').attr("aria-expanded","true");
        })
    });
    $('#togglenavB').click(function() {
        $('#navA').collapse('hide')
        $('#navB1').collapse('hide')
        $('#navB').collapse('toggle')
        $('#navB').on('hidden.bs.collapse', function () {
            $('#togglenavB').attr("aria-expanded","false");
        })
        $('#navB').on('shown.bs.collapse', function () {
            $('#togglenavB').attr("aria-expanded","true");
        })
    });
    $('#togglenavB1').click(function() {
        $('#navB1').collapse('toggle')
        $('#navB1').on('hidden.bs.collapse', function () {
            $('#togglenavB1').attr("aria-expanded","false");
        })
        $('#navB1').on('shown.bs.collapse', function () {
            $('#togglenavB1').attr("aria-expanded","true");
        })
    });
});

Note: even though this solution solves my problem, it does so at the cost of ignoring Bootstrap's already implementation of an accordion and removing some data-attributes from some links to avoid conflicts between some automatic functionalities from the pliguin with my custom code.

like image 42
gota Avatar answered Oct 01 '22 05:10

gota