2

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.

an accordion animation example

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:

https://codepen.io/anon/pen/ZwKMZx

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Tzar
  • 5,132
  • 4
  • 23
  • 57

1 Answers1

1

The issue all stems from the fact that you have:

dd {
    position: absolute;
    /* more styles */
}

Essentially, this makes it so that the height of this element gets ignored when calculating its parent's height.

I'm not familiar with the library you are using, but doing a quick modification to remove that, set the max-height to 0 when it's hidden, and then animate that height to the scrollHeight of the content (i.e. the height of the content when maxHeight is ignored).

https://codepen.io/anon/pen/xMXbJX

dd {
    opacity: 0;
    max-height: 0;
}

const fold = async (content, direction = "normal") => {
   const scrollHeight = content.scrollHeight
   await animate({
     ...timing,
     direction,
     elements: content,
     opacity: [0, 1],
     maxHeight: ['0px', scrollHeight + 'px'],
     transform: ["scaleY(0)", 1]
  })
}
dave
  • 62,300
  • 5
  • 72
  • 93
  • Hi, Dave. Thanks for taking the time to answer. In your solution, neither the `fieldset` nor other elements are animated. So the whole point of using Animate Plus is lost. – Tzar Feb 04 '19 at 23:26
  • 1
    ...yeah, as I said, it's going to jump until you put in animating the height. So, to be clear - figure out how to animate the height, using the animation library you are using. I'd imaging it works the same way you are animating the opacity. If you have trouble animating from 0 to auto, then you should keep height set to auto, and animate the maxHeight property from 0 to whatever large number you feel like. – dave Feb 05 '19 at 00:10