Animate the Details element

Original link: https://blog.guhub.cn/coder/how-to-animate-details.html

<details> is a new tag added to HTML5, and although it has been around for a while, it seems to be rarely mentioned and used. At present, in mainstream browsers, most browsers except IE browser support <details> tag, but some of them are not written strictly according to the specification, and there may be some bugs. Writing animations for <details> is difficult because of its specificity.

Why write animation

For a better user experience. The function of the <details> tag is to display a piece of text by default, and click to expand the details.

But this “expand” is very blunt. After clicking on the text, the arrow pointing to the left will become downward, and the hidden content will appear directly. These changes are completed in an instant. In the face of sudden content, especially when there is a lot of expanded content, users will have a feeling of “overwhelmed”. like this:

See the Pen
Default Details Element Demostration
by Eltrac Koalar ( @Eltrac )
on CodePen .

To fix this, we need to animate the <details> to make the element appear more natural and give the user more time to react.

Why the Details tab is hard to animate

The first is because of the structure of the <details> tag, which is what it is.

 <details> <summary>Click me!</summary> Now you see me! </details>

Among them, the part wrapped by the <summary> tag is the part that will be displayed by default, and the text directly wrapped in the <details> tag is the part that is hidden. Both of these parts are child elements of details, and it is difficult for us to specify selectors for each of them.

For example, we can directly use the summary selector to select the <summary> tag, but we can only use details * to select hidden content, and this selector will also select the <summary> tag.

Secondly, the small triangle displayed in front of the <summary> tag to indicate the opening and closing status of the tag is actually something similar to the small dot in front of the list, namely list-style-type ; in Chrome, it is a pseudo-element, That is ::marker (however this is not canonical). This feature makes it impossible for us to directly animate this small triangle, whether it is rotation, fading and other transformation methods.

Finally, the open or closed state of the <details> tag is determined by whether the tag has the open attribute. After clicking <summary> , its parent element, <details> , will be given the open attribute; the <details> tag with the open attribute will be opened, that is, the content inside it that is not in the <summary> tag will be displayed.

It’s puzzling how the <details> tag hides the content? Is the content removed or added directly from the DOM tree, or is the content’s display property changed? Neither seems to be. The browser’s inspection tools show that the content exists in the DOM tree regardless of whether the element has an open attribute; trying to look at the css properties of the corresponding content didn’t yield any clues. This seems to be a special way of rendering.

This is the most troublesome thing, we (at least me and the big guys I saw on StackOverflow) have absolutely no idea how the <details> tag works.

find a solution

To animate the <details> tag, we need to overcome the above problems.

The first is the problem of structure, which is very simple and violent. We only need to wrap the content that needs to be hidden with additional tags when writing the <details> tag. Here we use the <section> tag as an example.

 <details> <summary>Click me!</summary> <section>Now you see me!</section> </details>

This way we can select these two elements with details summary and details section respectively.

Then there is the problem of the little triangle, as long as we hide the triangle and insert a new, similar icon so we can style it however we want. To hide this little triangle, Chrome does something different than the standard way, so we need to define two selectors.

 details summary {list-style-type: none} details summary::-webkit-details-marker {display: none}

Then, create a new icon with the pseudo-element ::before , replace it with + , and turn it 45 degrees after opening to x , which means open and close respectively.

 details summary::before { content: '+'; transition: transform .3s; margin-right: .5em; display: inline-block /* 必须是块级元素才能够旋转*/ } details[open] summary::before {transform: rotate(45deg) }

In this way, the rotation transition animation of the icon is done, and the next step is the main event, that is, the animation of expanding and collapsing.

I tried changing the height property and adding a transition animation to achieve that, but it didn’t work.

 details section { height: 0; overflow: hidden; transition: height .3s } details[open] section {height: auto}

This may be due to the specificity of the <details> tag, as I said before, I don’t know how it works.

So I had to settle for the next best thing and use a fade animation.

 details[open] summary ~ * {animation: sweepIn .3s ease-in-out;} @keyframes sweepIn {0% {opacity: 0; transform: translateY(-10px); margin-bottom: -10px} 100% {opacity: 1; transform: translateY(0)} }

See the Pen
Roughly Animated Details Element
by Eltrac Koalar ( @Eltrac )
on CodePen .

The effect looks good this time, but… the animation only works on the first click, and the closing animation is just as stiff.

I’ve tried all sorts of things, and after looking for answers on various platforms like StackOverflow to no avail, I’ve had to use JavaScript to animate the <details> tag. Ah, I’m really convinced, isn’t the purpose of this tag just to make it easier to write accordions, why do you have to write JS yourself?

First of all, we have to have an idea. We don’t know how the browser handles the <details> tag, so we need to prevent the default behavior of the browser.

Since I am a lazy dog, please use the famous jQuery here.
 $('details').on("click",function(e){e.preventDefault();// 阻止details 直接显示内容});

Then we need to determine the operation after the click according to whether the <details> tag is opened, that is, whether there is an open attribute. The general idea is as follows.

When the <details> tag is clicked,

  • If there is an open attribute, add the .closing class to the element, and write a fade-out animation. After the animation ends, remove the .closing class and the open attribute.
  • If there is no open attribute, add the open attribute directly to the element, because we have written the fade-in animation for details[open]

The specific code is as follows

 $('details').on("click",function(e){e.preventDefault();// 阻止details 直接显示内容if(!$(this).attr('open')){$(this).attr('open',''); }else{$(this).addClass('closing'); setTimeout(() => {$(this).removeClass('closing'); $(this).removeAttr('open'); }, 300); } });
 details.closing section {animation: sweepOut .3s ease-in-out;} @keyframes sweepOut {0% {opacity: 1; transform: translateY(0)} 100% {opacity: 0; transform: translateY(-10px)} }

On this basis, some optimizations can be made, adding a negative margin-bottom to the last frame of the sweepOut animation, so that the text behind it can move up a certain distance with it, and it will look smoother.

 @keyframes sweepOut {0% {opacity: 1; transform: translateY(0);} 100% {opacity: 0; transform: translateY(-10px); margin-bottom: -1.5em} }

When the line spacing is 1.5, the text size is 1em, and the hidden content is only one line, the performance of this animation is almost perfect, but when the number of lines is relatively large, only the text below can be seen moving for a certain distance, and then directly It moved abruptly to its original position. But this is the best I can do.

Then, I found another problem: when the <details> tag is collapsed, the symbol + indicating expand/close will only rotate until the collapsed animation ends, and these two animations should be performed at the same time.

I thought this problem would require modifying JS to solve it, but after thinking about it, it only needs to change the css selector.

 details[open]:not(.closing) summary::before {transform: rotate(45deg) }

The key lies in this :not(.closing) , when the element does not have a closing class, it will rotate 45 degrees, and when the <details> tag is closed, the closing class is added, which happens to not meet this condition, so it changes back to 0 degrees Well, the process still has transition animations.

Final result

 $('details').on("click",function(e){e.preventDefault();// 阻止details 直接显示内容if(!$(this).attr('open')){$(this).attr('open',''); }else{$(this).addClass('closing'); setTimeout(() => {$(this).removeClass('closing'); $(this).removeAttr('open'); }, 300); } });
 details summary {list-style-type: none} details summary::-webkit-details-marker {display: none} details summary::before { content: '+'; transition: transform .3s; margin-right: .5em; display: inline-block /* 必须是块级元素才能够旋转*/ } details[open]:not(.closing) summary::before {transform: rotate(45deg) } details[open] summary ~ * {animation: sweepIn .3s ease-in-out;} @keyframes sweepIn {0% {opacity: 0; transform: translateY(-10px); margin-bottom: -10px} 100% {opacity: 1; transform: translateY(0)} } details.closing section {animation: sweepOut .3s ease-in-out;} @keyframes sweepOut {0% {opacity: 1; transform: translateY(0);} 100% {opacity: 0; transform: translateY(-10px); margin-bottom: -1.5em} }

Because jQuery is used, it is inconvenient to use codePen to show it. Just look at the effect on my blog.

click me click me click me click me

How about it, it’s silky enough (●ˇ∀ˇ●)

I also sent this code to the gist , if it helps you, give me a star.

postscript

This is the problem I encountered when adapting the CSS style of the BracketDown plugin to the Matcha theme. I felt that it was worth discussing, so I posted it. After writing this thing, my biggest feeling is: <details> is a garbage element.

How to say, although this thing is convenient, and it is still the HTML5 specification, it is called “future-proof”. But facing the future for such a long time, this thing is not optimized at all, and there are big problems in compatibility and customization. It is so troublesome to write an animation for it, and JS is required to write its animation, which is true It is better to write it yourself, it is much easier to use than the native one.

Lost a few more hairs, tired.

This article is reproduced from: https://blog.guhub.cn/coder/how-to-animate-details.html
This site is for inclusion only, and the copyright belongs to the original author.

Leave a Comment