DEV Community

Muhammad Sulais
Muhammad Sulais

Posted on

1

How I made toggle transition to <details> element

The HTML <details> element allows you to expand and shrink content effortlessly. Here's a basic implementation of <details>:

<details id="my-details">
  <summary>Click me to expand</summary>
  <!-- Your content here -->
</details>
Enter fullscreen mode Exit fullscreen mode

While this element is great, you might wonder how to add a smooth transition effect with CSS alone:

details#my-details {
  transition-duration: 300ms; 
}
Enter fullscreen mode Exit fullscreen mode

However, using just CSS doesn’t apply any transition effect when toggling the element. To achieve this, you need to use some JavaScript.

Solution

First, wrap the content within the <details> element with another element. In this example, we use a <div> as the content wrapper:

<details id="my-details">
  <summary>Click me to expand</summary>
  <div>
    <!-- Your content here -->
  </div>
</details>
Enter fullscreen mode Exit fullscreen mode

Although the animation typically works as expected, there are certain conditions where you might need to apply overflow: hidden to the content wrapper to achieve the desired effect.

Next, we add the JavaScript to handle the transition effect:

const details = document.getElementById("my-details");
const summary = details.firstElementChild;
const content = summary.nextElementSibling;
let isAnimating = false;

summary.onclick = (ev) => {
  ev.preventDefault();
  if (isAnimating) return;

  const contentHeight = content.getBoundingClientRect().height;
  isAnimating = true;

  // Closing <details>
  if (details.open) {
    return content
      .animate(
        { height: [contentHeight + 'px', '0px'] }, 
        { duration: 300 }
      )
      .finished
      .then(() => details.open = isAnimating = false);
  }

  // Opening <details>
  details.open = true;
  content.animate(
    { height: ['0px', contentHeight + 'px'] },
    { duration: 300 }
  ).finished.then(() => isAnimating = false);
};
Enter fullscreen mode Exit fullscreen mode

Now, when you toggle the <details>, it will animate the transition.

But wait, how exactly does this work?

Explanation

Let’s break down the JavaScript code. The simplest way to animate an element is by using the animate() method, which accepts two arguments: keyframes and options. We'll explore its details in another post, but here's a quick overview.

To achieve the animation, we prevent the default behavior of the <summary> element to disable its instant toggle functionality:

summary.onclick = (ev) => {
  ev.preventDefault();
};
Enter fullscreen mode Exit fullscreen mode

Next, we obtain the height of the <details> content:

const contentHeight = content.getBoundingClientRect().height;
Enter fullscreen mode Exit fullscreen mode

If the <details> is open, indicated by the open property, we animate the content height from its default height to 0px and set details.open to false after the animation ends:

// Closing <details>
if (details.open) {
  return content
    .animate(
      { height: [contentHeight + 'px', '0px'] }, 
      { duration: 300 }
    )
    .finished
    .then(() => details.open = isAnimating = false);
}
Enter fullscreen mode Exit fullscreen mode

When opening <details>, we:

  • Animate the content height from 0px to its default height.
  • Set details.open to true before the animation starts.
// Opening <details>
details.open = true;
content.animate(
  { height: ['0px', contentHeight + 'px'] },
  { duration: 300 }
).finished.then(() => isAnimating = false);
Enter fullscreen mode Exit fullscreen mode

Understanding the isAnimating Variable

The isAnimating variable is used to determine if an animation is currently running. If an animation is in progress, further toggle actions are prevented until the animation completes:

let isAnimating = false;

summary.onclick = (ev) => {
  ev.preventDefault();
  if (isAnimating) return;

  isAnimating = true; // mark as not done

  content.animate(/* ... */)
  .finished
  .then(() => isAnimating = false) // mark as done
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

These transition effects aren’t limited to height changes. You can use any CSS properties to animate the <details> element. The animate() method is a powerful way to create engaging, visually appealing web interactions.

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more