An accordion animated with Animate Plus contains dl and legend elements within fieldset. Everything works as it’s supposed to, except fieldset doesn’t expand and legend doesn’t move with the rest of the elements.
I would like to smoothly resize fieldset height by the same amount dl is increased.
My JavaScript code:
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"
My full code for an accordion can be found on CodePen:
