Last active
April 30, 2018 11:11
-
-
Save SigurdMW/18b861c5f7b48bc849c58ceb400359db to your computer and use it in GitHub Desktop.
accessible collapse and collapse group for keyboard usage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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