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.
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.