Open bendur opened 6 years ago
I wrote some code to do this a while back. You can see a working example at https://ssbhibbing.com/faq.html
<div class="js-accordion">
<!-- add this button to the accordion parent/wrapper element -->
<button type="button" class="expand-collapse" aria-pressed="false">
<span class="expand">Expand all</span>
<span class="collapse">Collapse all</span>
</button>
<div class="js-accordion__panel">
<h2 class="js-accordion__header">First tab</h2>
<p>here is the content of the 1st tab</p>
</div>
// and so on …
</div>
$('.expand-collapse').click(function(e) {
e.preventDefault();
// toggle this button's ARIA attribute and a status variable we'll use to only expand and collapse stuff that isn't already expanded or collapsed
var expandedStateToggle = 'false';
if($(this).attr('aria-pressed') === 'false') { // button is in the "off" state, so we're going to expand the accordions
$(this).attr('aria-pressed', 'true');
expandedStateToggle = 'false';
} else { // button is in the "on" state, so we're going to collapse the accordions
$(this).attr('aria-pressed', 'false');
expandedStateToggle = 'true';
}
// set up an event handler that will return focus to the Expand/Collapse button after we expand/collapse all of the accordion panels (because the accordion plugin leaves the focus on whichever accordion heading button was clicked, and we're about to click all of them at once)
// the .one() function is not a misspelled .on() function -- .one() fires only once
// (code based on http://blog.teamtreehouse.com/using-jquery-to-detect-when-css3-animations-and-transitions-end)
$(this)
.siblings('.js-accordion__panel')
.one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function() {
// NOTE: inside this event handler, $(this) refers to the panel element[s] that fired this event, so we have to use e.target to refer to the Expand/Collapse button
//console.log('transitions are done');
e.target.focus(); // return the focus to the Expand/Collapse button
});
// trigger the accordion heading buttons' click events to expand/collapse their associated panels, but only the ones that aren't already affected
$(this).siblings('.js-accordion__header[aria-expanded="' + expandedStateToggle + '"]').click();
// scroll the page back to Expand/Collapse button
$([document.documentElement, document.body]).animate({
scrollTop: $(this).offset().top - 15
}, 500);
});
The code is written with the assumption that there will be only one expand/collapse button within the .js-accordion
parent/wrapper. If you want to have two or more buttons, e.g. one at the top of the accordions and one at the bottom, then the code will need to be changed to keep buttons' states in sync. The way it's currently written, clicking the top button will expand all of the accordions, and the bottom button will still say Expand All
, and if you click that second button nothing will happen except the page will scroll a bit and the text will change to say Collapse All
, and then a second click on that button will collapse all of the accordions, but the top button will still say Collapse All
… and I'm sure you understand the problem.
However, the above code can handle the situation where there are multiple .js-accordion
sections on the page, because it's targeting sibling elements of the button to expand/collapse.
The part of the code that handles the ARIA attributes could perhaps be simplified by using https://github.com/marcus-herrmann/toggleAria but I haven't gotten around to that yet.
It would be nice to have an open/close all method to act on everything in the accordion at once.