Summary
This how to demonstrates how to stop a Bootstrap accordion from closing all tab which leaves an empty space in your page which can cause unsightly resizing.
The Problem
The Bootstrap accordion allows you to display information by having a series of tabs which can be clicked to display the information under them. When a tab is clicked it closes all other tabs ensuring only one tab / panel of information is open a t a time. If a user clicks the currently open tab, the tab closes itself, and doesn’t open any other tab (obviously).
This leaves a space in your page design where content used to be. This can cause your page to resize ( items below the accordion shifting up the page ) or looks really ugly if the space where the content used to be is now a big blank nothing.
The Solution
The obvious solution is to catch the click event of the tab and, once you have checked the tab is trying to close itself, using event.preventDefault() to cancel the event. Unfortunately this doesn’t work. Its in the wrong part of the event tree and determining if the item clicked is the item closing is impossible ( or if it is possible I couldn’t see how without using Globals and other nasty JS kludges).
There are four other events we can hook into: Bootstrap Collapse
Event Type | Description |
---|---|
show.bs.collapse | This event fires immediately when the show instance method is called. |
shown.bs.collapse | This event is fired when a collapse element has been made visible to the user (will wait for CSS transitions to complete). |
hide.bs.collapse | This event is fired immediately when the hide method has been called. |
hidden.bs.collapse | This event is fired when a collapse element has been hidden from the user (will wait for CSS transitions to complete). |
- Unfortunately the hide.bs.collapse, which fires when the hide method has been called, is too late in the process for us to use event.preventDefault to stop it.
- Interestingly the hidden.bs.collapse fires before the collapse / hide is complete, which means we can check to see if any of the collapsing elements has a class of collapsing.
- If clicking on a tab is closing another tab, and opening itself, then the tab being closed will have a class of collapsing applied to it.
- If you click on the currently open tab to close it, it does not get a class of collapsing applied to it.
- This means we can can check all our collapsible elements to see if they have a class of collapsing applied to them. If they do then we know you are not closing the currently open tab by clicking on it.
- If there are no elements with a class of collapsing then you know the tab you are closing (by clicking on it) is the currently open one.
As such, using the code below we:
- Add an event listener to all collapsible elements
- Add a helper function that has all the code required to reset the (now closed) tab back to open
- Set all the elements to fire a function that checks if there are any collapsible elements with the collapsing class in it.
I know the helper function is redundant, but in my situation I have more than one panel / content block closing, so I need to reset more than one and the helper function stops me repeating code.
Enjoy:
<script> // Add an event listener to collapsable items document.addEventListener("DOMContentLoaded", function () { // Helper function to show a div function showCollapse(element) { // Add the 'show' class to display the element element.classList.add('show'); } // Add event listener to each collapsible element for the hidden.bs.collapse event document.querySelectorAll('.accordion-collapse').forEach(function (element) { element.addEventListener('hidden.bs.collapse', function (event) { //Event function fires after collapse and checks if any items are in the collapsing state // hidden.bs.collapse is after the item is collapsed but before the collapse event is complete // so we check for collapsing. If item is collapsing self there is no collapsing class added // Get all elements with the collapsing class let openCollapsibleLength = document.querySelectorAll('.accordion-collapse.collapsing').length; if (openCollapsibleLength === 0) { showCollapse(element); // reopen the collapsing item // Also reopen the other content element which has a class equal to the elements id let pictureElement = document.querySelector('.accordion-image__outer.' + element.id) showCollapse(pictureElement); } // if openCollapsibleLength === 0 }); // element.addEventListener }); // collapsibleElements.foreach - add listener }); // document.addEventListener( </script>