jQuery simple and accessible hide-show system (collapsible regions), using ARIA

Find/fork me on Github

Other accessible plugins

The jQuery plugin will add a shiny and accessible hide/show system (collapsible region), using ARIA.

Here are how-to-use and some examples of this plugin. Should I tell you to click and expand/collapse sections? :)

What is it?

It is a very simple jQuery script that transforms some contents into accessible collapsible regions.

It is lightweight: 1.9kb(development), 0.92kb (minified), only 383 bytes minified and gzipped.

No license problem, it uses MIT license, so it’s free, open-source and you can do whatever you want with it, including commercial use. This permission notice shall be included in all copies or substantial portions of it.

However, it is not prohibited to tell me that you’ve used it, or send me a little “thank you”. ;)

Why accessible and robust?

Accessible

Keyboard navigation is supported, based on Heydon’s progressive collapsible example and inspired by ARIA Design Pattern for Hide/Show regions.

You can open it using Enter or Space.

Robust

As it relies on a normal HTML structure (Hx), even if the JavaScript isn’t loaded, your page will work, it’s another great miracle of progressive enhancement.


What does it do?

Basically, it transforms this:

<h2 class="js-expandmore">Lorem dolor si amet</h2>
<div class="js-to_expand">
   here the hidden content
</div>

Into this:

<h2 class="js-expandmore">
  <button aria-controls="expand_1" aria-expanded="false" class="expandmore__button">
   <span class="expandmore__symbol" aria-hidden="true"></span>
   Lorem dolor si amet
  </button>
</h2>
<div id="expand_1" class="js-to_expand" aria-labelledby="label_expand_1">
   here the hidden content
</div>

Attribute values are generated on-the-fly (aria-controls="expand_1", id="expand_1", aria-labelledby="label_expand_1"), no need to worry about it.


How it works

Just follow this convention:

<h2 class="js-expandmore">Lorem dolor si amet</h2>
<div class="js-to_expand">
   here the hidden content
</div>

For accessibility reasons, you shall add class="expandmore" on an Hx (h1, h2, h3, etc.).
Elements that have expandmore and to_expand classes must be adjacent.

Then use the plugin, it will do the rest.

Or you may use npm i jquery-accessible-hide-show-aria, or if you prefer bower install jquery-accessible-hide-show-aria

Styles needed to work

The minimal style needed is:

.js-to_expand[aria-hidden=true],
.js-to_expand[data-hidden=true] {
  display: none;
}

However, as the plugin adds a button into a Hx, you will have to style this case. Here is the example of this page.

.expandmore__button {
  background: none;
  font-size: inherit;
  color: inherit;
}
/* optional */
.expandmore__symbol:before {
  content : '+ ';
}
.expandmore__button[aria-expanded=true] > .expandmore__symbol:before,
.expandmore__button[data-expanded=true] > .expandmore__symbol:before {
  content : '− ';
}

Last updates

19th of June 2019: fixed “Expand/collapse all” button issue, thanks to @llahnoraa.

27th of August 2018: added options to specify expand/collapse all texts (data-text-expand-all and data-text-close-all), added option data-not-all-expands="true".

28th of January 2018: fixed a vocalisation issue (pseudo-element can be vocalized), added a span class="expandmore__symbol" aria-hidden="true" inside generated button (which fixes the previous problem), thanks to @AnneCav.

4th of July 2017: added option to collapse/expand all items, as kindly suggested by @llahnoraa and @Kozlika.

3rd of July 2017: added option to allow only one hide/show opened at a time, option added by @kloh-fr.

16th of April 2017: linted (fix missing var declaration, etc.) and re-indented properly.

3rd of March 2017: added type="button" to buttons (avoid issues in forms/sharepoint) thanks to @StaudenmannM, and removed unecessary selectors thanks to @restersysteme.


Bonus: opened by default

Wait wait wait, I want to have this system, but with a region that is not collapsed by default! How should I do?

No problem, it is possible and very simple, just use the class is-opened on:

<h2 class="js-expandmore">Lorem dolor si amet</h2>
<div class="js-to_expand is-opened">
   here the hidden content
</div>

The plugin will detect it, and make it open by default. As simple as one copy/paste.


Bonus: anchor detection

Wait wait wait, I want to use it, but I have a link to an id that is in a section collapsed by default! How should I do?

No problem, the plugin will detect it, and make it open by default. Here is a link to test it: https://a11y.nicolas-hoffmann.net/hide-show/#answer-anchor
(open it in a new tab or window to see the magic)


Bonus: different styles

Wait wait wait, I have a special need for styling, how could I do it?

No problem, it is possible and very simple, just use the attribute data-hideshow-prefix-class="<your_value>" like this:

<h2 class="js-expandmore" data-hideshow-prefix-class="mini-combo">Lorem dolor si amet</h2>
<div class="js-to_expand">
   here the hidden content
</div>

It will prefix generated classes, <your_value>-expandmore__button and <your_value>-expandmore__to_expand, like this:

<h2 class="js-expandmore" data-hideshow-prefix-class="mini-combo">
 <button id="label_expand_2" class="mini-combo-expandmore__button js-expandmore-button" aria-controls="expand_2" aria-expanded="false">
  Lorem dolor si amet
 </button>
</h2>
<div id="expand_2" class="js-to_expand mini-combo-expandmore__to_expand" aria-hidden="true" aria-labelledby="label_expand_2">
 here the hidden content
</div>

The plugin will prefix all classes, so you will able to style elements as you want. If you don’t use it, the plugin won’t mind.


Bonus: one button to collapse/expand them all

Wait wait wait, I need an option to collapse/expand all items.

A special option has been added to the script (you have an example at the top of this page): if you need this kind of button, do like this:

<button class="js-expandmore-all" data-text-expand-all="Expand all" data-text-close-all="Collapse all">Collapse all</button>

The default action is a collapse. If you need an expand by default, you may set it up like this:

<button type="button" class="js-expandmore-all" data-expand="true">Expand all</button>

Default texts can be updated in the script parameters: expand_all_text and collapse_all_text or specified for each case in data-text-expand-all and data-text-close-all attributes on the button.

If you want a section not to be impacted by this button, specify data-not-all-expands="true" on the element with .js-expandmore.


Bonus: some animations?

Wait wait wait, I want to animate when I open or close something, how could I do it?

No problem, it is possible using some CSS transitions. You have to keep in mind several things to keep it accessible:

If you have clicked on this section, you might have noticed the animation. Let’s assume we are using this source:

<h2 class="js-expandmore mb0 mt0" data-hideshow-prefix-class="animated">Bonus: some animations?</h2>
 <div class="js-to_expand">
 …
 </div>

So here is the CSS code (unprefixed):

/* This is the opened state */
.animated-expandmore__to_expand {
 display: block;
 overflow: hidden;
 opacity: 1;
 transition: visibility 0s ease, max-height 2s ease, opacity 2s ease ;
 max-height: 80em;
 /* magic number for max-height = enough height */
 visibility: visible;
 transition-delay: 0s;
}
/* This is the hidden state */
[data-hidden=true].animated-expandmore__to_expand {
 display: block;
 max-height: 0;
 opacity: 0;
 visibility: hidden;
 transition-delay: 2s, 0s, 0s;
}

Here is the trick: from “hidden” to “visible” state, visibility is immediately set up to visible, and max-height/opacity are “normally” animated.

From “visible” to “hidden” state, the visibility animation is delayed. So the content will be immediately hidden… at the end of the animation of max-height/opacity.


Bonus: ARIA attributes or data attributes?

Wait wait wait, I do have special requirements, how may I tweak attributes for my special case?

No problem. At the beginning of the plugin, you can set up the attributes you need for your special case.

   var attr_control = 'data-controls',
    attr_expanded = 'aria-expanded',
    attr_labelledby = 'data-labelledby',
    attr_hidden = 'data-hidden',
    $expandmore = $('.js-expandmore'),
    $to_expand = $('.js-to_expand');

However, the default settings are recommended by accessibility experts.

If you need to update, you can do it anyway, the plugin will do the rest. Of course, you will have to update your CSS by using the good attributes (.js-to_expand[data-hidden=true]).


Older updates

21th of February 2017: added a feature that detects an anchor to a collapsed section (and opens it), thanks to @llahnoraa (have a look at “Bonus: anchor detection” below).

5th of February 2017: fixed an issue to avoid WordPress jQuery conflict mode thanks to @floriantiar.

22nd of June 2016: fixed an issue on Chrome thanks to @Kozlika. This plugin is now available on Bower and can be installed using bower install jquery-accessible-hide-show-aria

21st of June 2016: updated default settings for better accessibility in browsers, thanks to @goetsu and @simonsbart.

7th of February 2016: This plugin is available on NPMjs.com and can be installed using npm i jquery-accessible-hide-show-aria.

28th of January 2016: added an option to avoid bugs for slow pages/very big DOM (thanks to @ffoodd).

1st of December 2015: added a section on animations.

25th of November 2015: added possibility to use ARIA attributes or data attributes (request by @goetsu).

23rd of August 2015: added click detection on Hx (fixes a VoiceOver bug).

10th of August 2015: added data-hideshow-prefix-class attribute (prefix classes), added js-classes elements.