Search code examples
javascriptaccordion

How can I make this accordion collapsed by default?


I'd like to use the javascript that W3C provides here for an accordion. My problem is that the panels are expanded by default. I would like them to be collapsed when the page loads, and I can't figure out how to change it.

javascript:

'use strict';

      class Accordion {
        constructor(domNode) {
          this.rootEl = domNode;
          this.buttonEl = this.rootEl.querySelector('button[aria-expanded]');

          const controlsId = this.buttonEl.getAttribute('aria-controls');
          this.contentEl = document.getElementById(controlsId);

          this.open = this.buttonEl.getAttribute('aria-expanded') === 'true';

          // add event listeners
          this.buttonEl.addEventListener('click', this.onButtonClick.bind(this));
        }

        onButtonClick() {
          this.toggle(!this.open);
        }

        toggle(open) {
          // don't do anything if the open state doesn't change
          if (open === this.open) {
            return;
          }

          // update the internal state
          this.open = open;

          // handle DOM updates
          this.buttonEl.setAttribute('aria-expanded', `${open}`);
          if (open) {
            this.contentEl.removeAttribute('hidden');
          } else {
            this.contentEl.setAttribute('hidden', '');
          }
        }

        // Add public open and close methods for convenience
        open() {
          this.toggle(true);
        }

        close() {
          this.toggle(false);
        }
      }

      // init accordions
      const accordions = document.querySelectorAll('.accordion h3');
      accordions.forEach((accordionEl) => {
        new Accordion(accordionEl);
      });

HTML:

<div id="accordionGroup" class="accordion">
      <h3>
        <button type="button"
                aria-expanded="false"
                class="accordion-trigger"
                aria-controls="sect1"
                id="accordion1id">
          <span class="accordion-title">
            Section 1
            <span class="accordion-icon"></span>
          </span>
        </button>
      </h3>
      <div id="sect1"
           role="region"
           aria-labelledby="accordion1id"
           class="accordion-panel">
        <div>
          <div class="accordion-content">
            Section 1 content
          </div>
        </div>
      </div>
    </div>

I made sure that aria-expanded="false", but it's still expanded when the page loads.


Solution

  • looking at the code it doesn't add the hidden state for you, so you'd either have to manually add it, or use something like the following

    constructor(domNode) {
        this.rootEl = domNode;
        this.buttonEl = this.rootEl.querySelector('button[aria-expanded]');
    
        const controlsId = this.buttonEl.getAttribute('aria-controls');
        this.contentEl = document.getElementById(controlsId);
    
        this.open = this.buttonEl.getAttribute('aria-expanded') === 'true';
    
        // make sure hidden elements are actually hidden
        if (!this.open && !this.contentEl.getAttribute("hidden")) { 
            this.contentEl.setAttribute("hidden", "")
        }
    
        // add event listeners
        this.buttonEl.addEventListener('click', this.onButtonClick.bind(this));
    }
    

    update

    checking if the accordion is closed before adding the hidden attribute avoids conflicting states.

    if hidden is set, and aria-expanded is also set, or the opposite state, you will have to press the button twice. the first press syncs the attributes, and the second actually does the action you wanted.