Skip to content

Instantly share code, notes, and snippets.

@SigurdMW
Last active April 30, 2018 11:11
Show Gist options
  • Save SigurdMW/18b861c5f7b48bc849c58ceb400359db to your computer and use it in GitHub Desktop.
Save SigurdMW/18b861c5f7b48bc849c58ceb400359db to your computer and use it in GitHub Desktop.
accessible collapse and collapse group for keyboard usage
class Accordation {
constructor ({ el, openOnLoad = false, id }) {
this.el = el;
this.openOnLoad = openOnLoad;
this.id = id;
this.trigger = null;
this.body = null;
this.isOpen = openOnLoad;
this.init();
}
init () {
this.trigger = this.el.querySelector(".js-accordation__trigger");
this.body = this.el.querySelector(".js-accordation__body");
if (this.trigger && this.body && this.id !== null) {
this.setUpElements();
} else {
throw new Error("Trigger, body or id not found on component. Both properties are required for accordation.");
}
}
handleClick () {
this.toggle();
}
setUpElements () {
this.trigger.setAttribute("aria-expanded", this.openOnLoad ? "true" : "false");
this.trigger.setAttribute("aria-controls", "accordation-body-" + this.id);
this.trigger.setAttribute("id", "accordation-trigger-" + this.id);
this.body.setAttribute("aria-labelledby", "accordation-trigger-" + this.id);
this.body.setAttribute("aria-hidden", this.openOnLoad ? "false" : "true");
this.trigger.addEventListener("click", () => {
this.handleClick();
});
this.trigger.addEventListener("keyup", (e) => {
if (e.keyCode === 32) {
this.handleClick();
}
});
}
open () {
this.isOpen = true;
this.trigger.setAttribute("aria-expanded", "true");
this.body.setAttribute("aria-hidden", "false");
this.el.classList.add("accordation--open");
}
close () {
this.isOpen = false;
this.trigger.setAttribute("aria-expanded", "false");
this.body.setAttribute("aria-hidden", "true");
this.el.classList.remove("accordation--open");
}
toggle () {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
}
class AccordationGroup {
constructor ({ el, firstOpen = false, id }) {
this.el = el;
this.firstOpen = firstOpen;
this.id = id;
this.state = [];
this.init();
}
init () {
if (this.el) {
const accordations = this.el.querySelectorAll(".accordation");
if (accordations.length && this.id) {
accordations.forEach((accordation, index) => {
try {
const opt = {
el: accordation,
openOnLoad: false,
id: this.id + "-" + index
};
if (this.firstOpen) opt.openOnLoad = true;
const accInstance = new Accordation(opt);
this.state.push(accInstance);
} catch (e) {
console.warn(e);
}
});
this.handleKeyEvents();
} else {
throw new Error("No accordations detected or id is not present.");
}
}
}
handleKeyEvents () {
// to disable scroll on arrow down key
this.el.addEventListener("keydown", (e) => {
if (e.keyCode === 38 || e.keyCode === 40) e.preventDefault();
});
this.el.addEventListener("keyup", (e) => {
e.preventDefault();
const hasFocus = this.state.some((ins) => ins.trigger === document.activeElement);
if (hasFocus) {
let activeIndex = null;
for (let i = 0; i < this.state.length; i++) {
if (this.state[i].trigger === document.activeElement) {
activeIndex = i;
}
}
if (activeIndex !== null) {
if (e.keyCode === 38) {
this.moveFocusDown(activeIndex - 1);
} else if (e.keyCode === 40) {
this.moveFocusUp(activeIndex + 1);
}
}
}
});
}
moveFocusUp (index) {
if (index > this.state.length - 1) {
this.state[0].trigger.focus();
} else {
this.state[index].trigger.focus();
}
}
moveFocusDown (index) {
if (index < 0) {
this.state[this.state.length - 1].trigger.focus();
} else {
this.state[index].trigger.focus();
}
}
}
// Init accordatiin / acc. group
function initAccordation() {
const accordations = document.querySelectorAll(".js-accordation");
if (accordations.length) {
let parent = null;
accordations.forEach((accordation, index) => {
const nonZeroIndex = index + 1;
if (accordations[index].parentElement.classList.contains("accordation-group__items")) {
if (!parent || parent !== accordations[index].parentElement) {
parent = accordations[index].parentElement;
try {
new AccordationGroup({
el: parent,
firstOpen: false,
id: nonZeroIndex
});
} catch (e) {
console.warn(e);
}
}
} else {
try {
new Accordation({
el: accordation,
openOnLoad: false,
id: nonZeroIndex
});
} catch (e) {
console.warn(e);
}
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment