DEV Community

Muhammad Sulais
Muhammad Sulais

Posted on

1 1 2

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.

Image of AssemblyAI tool

Challenge Submission: SpeechCraft - AI-Powered Speech Analysis for Better Communication

SpeechCraft is an advanced real-time speech analytics platform that transforms spoken words into actionable insights. Using cutting-edge AI technology from AssemblyAI, it provides instant transcription while analyzing multiple dimensions of speech performance.

Read full post

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay