Collapsable.js: Vanilla JS Accordions & Content Toggles

Category: Accordion , Javascript | October 12, 2026
Authorzipper
Last UpdateOctober 12, 2026
LicenseMIT
Views0 views
Collapsable.js: Vanilla JS Accordions & Content Toggles

Collapsable.js is a vanilla JavaScript library for creating expandable HTML content panels and accordions.

It works with existing HTML and supports independent toggles, single-open groups, and collapsible sections that activate at specific screen widths.

Features

  • Open a specific panel on page load through a CSS class or URL fragment.
  • Keep at least one panel expanded when required.
  • Toggle panels from external links or buttons.
  • Configure responsive activation with CSS media queries.
  • Cancel an opening or closing action through a DOM event.
  • Customize the CSS classes used for open and closed states.
  • Update ARIA attributes on interactive controls and content panels.
  • Receive completion events after CSS animations finish.

How To Use It

Installation

Load the latest Collapsable.js in the document. You can write your own CSS styles for the panel controls, state indicators, and transitions.

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/Collapsable.min.js"></script>

npm and ES Modules

For a project with a JavaScript bundler, install the package and import its default export.

npm install collapsable.js
import Collapsable from 'collapsable.js';

Basic Usage

Each collapsible item requires an HTML root, a control element, and a content box.

The default control and box classes are .js-collapsable__control and .js-collapsable__box. An explicit <button> makes the control keyboard-operable through native button behavior. If a control has no button or anchor, Collapsable.js creates a button for it.

<div id="shipping" class="js-collapsable">
  <h3>
    <button type="button" class="js-collapsable__control">
      Shipping information
    </button>
  </h3>
  <div class="js-collapsable__box">
    <p>Orders usually ship within two business days.</p>
  </div>
</div>
<div id="returns" class="js-collapsable">
  <h3>
    <button type="button" class="js-collapsable__control">
      Returns and refunds
    </button>
  </h3>
  <div class="js-collapsable__box">
    <p>Contact support within 30 days of delivery.</p>
  </div>
</div>

Initialize the library after the relevant markup is available. The Collapsable constructor accepts one HTMLElement or a NodeList of elements, followed by an optional configuration object.

const panels = new Collapsable(
  document.querySelectorAll('.js-collapsable')
);

Single-Open Accordions

Set accordion: true to close the previous panel when another opens. For a group that must always have one expanded item, also set collapsableAll: false.

The js-collapsable--default-expanded class determines which item opens initially. When no item is marked and all panels cannot close, the first item opens.

<div id="account" class="js-collapsable js-collapsable--default-expanded">
  <h3><button type="button" class="js-collapsable__control">Account</button></h3>
  <div class="js-collapsable__box"><p>Account settings.</p></div>
</div>
<div id="privacy" class="js-collapsable">
  <h3><button type="button" class="js-collapsable__control">Privacy</button></h3>
  <div class="js-collapsable__box"><p>Privacy preferences.</p></div>
</div>
const accordion = new Collapsable(
  document.querySelectorAll('#account, #privacy'),
  { accordion: true, collapsableAll: false }
);

External Controls

External anchors use a URL fragment that matches the root item’s id. External buttons use data-collapsable-id. Insert these elements before the initialization script in Basic Usage. The default selector is .js-collapsable-ext-link. The following controls work with the shipping and returns panels from Basic Usage.

The data-collapsable-prevent-default attribute stops an external anchor from changing the browser’s location when clicked. Use the externalLinks.preventDefault option if this behavior should apply to every external control.

<a href="#shipping" class="js-collapsable-ext-link"
   data-collapsable-prevent-default>
  Toggle shipping information
</a>
<button type="button" class="js-collapsable-ext-link"
        data-collapsable-id="returns">
  Toggle returns
</button>

Responsive Activation

A panel with data-collapsable-media-query initializes its toggle behavior only while the media query matches. For mobile navigation, show the content normally on larger screens and conceal the unused control there. The example below operates as a collapsible section below 768px.

<div id="mobile-nav" class="js-collapsable"
     data-collapsable-media-query="(max-width: 767px)">
  <h3><button type="button" class="js-collapsable__control">Categories</button></h3>
  <div class="js-collapsable__box">
    <a href="/products/">Products</a>
    <a href="/support/">Support</a>
  </div>
</div>
@media (min-width: 768px) {
  #mobile-nav .js-collapsable__control {
    display: none;
  }
}
new Collapsable(document.getElementById('mobile-nav'));

All Configuration Options

  • control (string, default '.js-collapsable__control:not(:scope .js-collapsable .js-collapsable__control)'): Selects the controls inside an item.
  • box (string, default '.js-collapsable__box:not(:scope .js-collapsable .js-collapsable__box)'): Selects the item’s content boxes.
  • event (string, default 'click'): DOM event used to toggle a control.
  • preventDefault (boolean, default true): Cancels the control event’s default browser action.
  • eventDelayTimeout (number | undefined, default undefined): Maximum delay in milliseconds for a completion event while animations run. An undefined value waits for the tracked animations to finish.
  • eventDelayGetAnimationsWithSubtree (boolean, default false): Includes animations on descendants when checking for completion.
  • accordion (boolean, default false): Permits a maximum of one expanded item in a group.
  • collapsableAll (boolean, default true): Permits every item to close. Set to false to keep at least one open.
  • externalLinks.selector (string, default '.js-collapsable-ext-link'): Finds external anchors or buttons. An empty string disables external control discovery.
  • externalLinks.preventDefault (boolean, default false): Cancels the default action for external control clicks.
  • classNames.interactiveElement (string, default 'js-collapsable__button'): Class applied to a control’s interactive button or anchor.
  • classNames.expanded (string, default 'js-collapsable--expanded'): Class applied to an expanded item root.
  • classNames.collapsed (string, default 'js-collapsable--collapsed'): Class applied to a collapsed item root.
  • classNames.defaultExpanded (string, default 'js-collapsable--default-expanded'): Class indicating that an item should open on initialization.
  • classNames.externalLinkActive (string, default 'js-collapsable-ext-link--active'): Class applied to external controls associated with an open item.

HTML Classes and Data Attributes

ClassPurpose
.js-collapsableRoot class used to identify nested collapsible items in the default selectors.
.js-collapsable__controlElement containing the interactive toggle control.
.js-collapsable__boxCollapsible content box.
.js-collapsable__buttonClass applied to the interactive button or anchor.
.js-collapsable--default-expandedMarks a panel as initially open.
.js-collapsable--expanded / .js-collapsable--collapsedState classes applied to item roots.
.js-collapsable-ext-linkMarks an external toggle control.
.js-collapsable-ext-link--activeMarks an external control associated with an open item.

The following HTML attributes configure responsive behavior, default-open behavior, and external controls. During state changes, Collapsable.js manages hidden and aria-hidden on content boxes and aria-expanded and aria-controls on interactive controls. External elements with role="tab" receive aria-selected, including buttons with that role. This does not implement the complete keyboard interaction model for a tab widget.

AttributePurpose
data-collapsable-media-queryActivates the item when its CSS media query matches.
data-collapsable-default-expanded-mediaOpens the item initially when its CSS media query matches, provided no default-expanded class is set.
data-collapsable-idConnects an external button to an item root ID.
data-collapsable-prevent-defaultCancels the default action on an individual external control when the attribute is present.

Public Methods and Item API

const instance = new Collapsable(
  document.querySelectorAll('.js-collapsable')
);
instance.expandAll();
instance.collapseAll();
const shipping = instance.getItemById('shipping');
shipping?.expand(new Event('programmatic'), null, false);
console.log(instance.getExpanded());
instance.destroy();
MethodDescription
expandAll(data?)Attempts to open the group’s items. In accordion mode, opens at most one. The optional data is included in event detail. Returns void.
collapseAll(data?)Attempts to close all open items, subject to collapsableAll. Returns void.
getExpanded()Returns an array of open CollapsableItem objects whose media queries currently match.
getItemById(id?)Returns the item with the specified root ID, or undefined.
getExtLinkById(id?)Returns the external control objects associated with an item ID.
handleDefaultExpanded(media?)Reapplies default-open processing, optionally for a MediaQueryList. This is also called during responsive initialization. Returns void.
destroy()Removes the group’s item and external-control listeners. Returns void.

Group and Item Members

A Collapsable instance exposes the read-only options configuration and items array. getItemById() returns a CollapsableItem, also available as element.collapsableItem on an initialized item root. The properties and operations below are exposed on each item.

Item memberDescription
expand(event, data, force)Attempts to open the item. Returns boolean. Setting force to true bypasses cancelation of the opening event.
collapse(event, data, force)Attempts to close the item. Returns boolean. The last-open restriction applies.
destroy()Removes the item’s own event handlers and tracked state. Returns void.
isExpandedRead-only current expanded state (boolean).
isDefaultExpandedRead-only result of the initial-state rules (boolean).
idRead-only item root ID (string).
elementRead-only root HTML element.
controlElementsRead-only array of the item’s control elements.
controlInteractiveElementsRead-only array of its interactive buttons and anchors.
boxElementsRead-only array of associated content boxes.
mediaRead-only MediaQueryList or null for unrestricted items.
collapsableRead-only parent Collapsable instance.

Events

const shippingPanel = document.getElementById('shipping');
shippingPanel.addEventListener('expand.collapsable', (event) => {
  if (shippingPanel.dataset.locked === 'true') {
    event.preventDefault();
  }
});
shippingPanel.addEventListener('expanded.collapsable', (event) => {
  console.log('Trigger:', event.detail.collapsableEvent?.type);
  console.log('Data:', event.detail.data);
});
EventWhen it fires
expand.collapsableImmediately before an item opens. Cancelable.
expanded.collapsableAfter an item finishes opening.
collapse.collapsableImmediately before an item closes. Cancelable.
collapsed.collapsableAfter an item finishes closing.
destroy.collapsableWhen an item is destroyed.

Styling and Customization

Collapsable.js does not include a required CSS theme. The library applies state classes and updates the content’s hidden attribute, so you can style arrows, borders, colors, and transitions to match your page.

.js-collapsable__control {
  cursor: pointer;
}
.js-collapsable__control::after {
  content: ' +';
}
.js-collapsable--expanded .js-collapsable__control::after {
  content: ' −';
}

Alternatives and Related Resources

You Might Be Interested In:


Leave a Reply