Here is an updated CodePen using Array.from(document.querySelectorAll('dl'))
to target every dl
in the document:
https://codepen.io/jo_va/pen/OdVpbJ
Since there are now multiple accordions, I first build an array of accordions and I modified all almost functions to take an accordion and a list of buttons as first parameters.
At the end, you just have to iterate over the accordions and execute the same logic you had, but parameterized for the current accordion and its buttons.
const accordions = Array.from(document.querySelectorAll("dl")).map(dl => ({
element: dl,
translate: 0
}))
const getButtons = accordion => Array.from(
accordion.element.getElementsByTagName("button"),
element => ({
element,
translate: 0
})
)
const timing = {
easing: "out-quartic",
duration: 400
}
const clear = element =>
Object.values(element.attributes).forEach(({ name }) =>
element.removeAttribute(name)
)
const hide = async (accordion, buttons, collapsing) => {
const objects = buttons.filter(({ translate }) => translate)
const direction = "reverse"
rotate(collapsing.previousElementSibling.lastElementChild, direction)
slide(accordion, objects)
await fold(collapsing, direction)
clear(collapsing)
}
const show = (accordion, buttons, expanding) => {
const button = expanding.previousElementSibling.lastElementChild
const index = buttons.findIndex(({ element }) => element == button)
const objects = buttons.slice(index + 1)
const { height } = expanding.getBoundingClientRect()
expanding.className = "open"
rotate(button)
slide(accordion, objects, height)
fold(expanding)
}
const slide = (accordion, array, to = 0) => {
center(accordion, to)
animate({
...timing,
elements: array.map(({ element }) => element.parentElement),
transform(index) {
const object = array[index]
const from = object.translate
object.translate = to
return [`translateY(${from}px)`, to]
}
})
}
const center = (accordion, height) => {
const from = accordion.translate
const to = Math.round(-height / 2)
accordion.translate = to
animate({
...timing,
elements: accordion.element,
transform: [`translateY(${from}px)`, to]
})
}
const fold = async (content, direction = "normal") =>
await animate({
...timing,
direction,
elements: content,
opacity: [0, 1],
transform: ["scaleY(0)", 1]
})
const rotate = ({ lastElementChild: elements }, direction = "normal") =>
animate({
elements,
direction,
easing: "out-cubic",
duration: 600,
transform: ["rotate(0turn)", 0.5]
})
const toggle = (accordion, buttons) => async ({ target }) => {
const collapsing = accordion.element.querySelector(".open")
const expanding = target.parentElement.nextElementSibling
if (collapsing) await hide(accordion, buttons, collapsing)
if (collapsing != expanding) show(accordion, buttons, expanding)
}
accordions.forEach(accordion => {
const buttons = getButtons(accordion)
buttons.forEach(
({ element }) => element.addEventListener("click", toggle(accordion, buttons))
)
})
import animate from "https://cdn.jsdelivr.net/npm/animateplus@2/animateplus.js"
I hope that I correctly understood your problem and that helps you.
Cheers.