Home > Web Front-end > CSS Tutorial > Animation Techniques for Adding and Removing Items From a Stack

Animation Techniques for Adding and Removing Items From a Stack

Jennifer Aniston
Release: 2025-03-19 10:49:25
Original
554 people have browsed it

Animation Techniques for Adding and Removing Items From a Stack

CSS animation is simple and easy to use, but complex scenes are extremely challenging. For example, change the background color of the button when the mouse is hovering? Simple. But animation the position and size of elements in a high-performance way and affect the position of other elements? Tough! This article will explore this issue in depth.

A common example is to remove an item from a bunch of items . The items stacked on the top need to be moved downward to make up for the gaps in the items removed from the bottom of the stack. This is how it works in real life, and users may expect this realistic movement on the website. If not, users may feel confused or temporarily lost. Based on life experience, you expect something to work in a certain way and get completely different results, and the user may need extra time to deal with this unrealistic movement.

Here is a UI for adding an item (clicking a button) or removing an item (clicking a project).

You can slightly mask the bad UI by adding "fade out" animations, etc., but the result won't be great because the list will suddenly collapse and cause the same cognitive problems.

Applying CSS-only animations to dynamic DOM events (adding brand new elements and removing elements completely) is extremely tricky . We will face this problem directly and introduce three distinct animation types to deal with this problem, all of which achieve the goal of helping users understand changes in the project list. After completing this article, you will be able to use these animations, or build your own animations based on these concepts.

We will also briefly cover accessibility and how to make granular HTML layouts still retain some compatibility with auxiliary devices with ARIA properties.

Slide fade animation

A very modern approach (and my personal favorite) is to fade in and out vertically depending on where they will eventually arrive. This also means that the list needs to be "opened" a position (also animated) to make room for it. If an element leaves the list, the space it occupies needs to shrink.

Since we do many different operations at the same time, we need to change the DOM structure to wrap each .list-item into a properly named .list-container container class. This is absolutely crucial to make our animation work.

Copy after login
Copy after login
Copy after login
  • List Item
  • List Item
  • List Item
  • List Item

Now, this style is unorthodox, because in order for our animation effects to work later, we need to style the list in a very specific way, which is done at the expense of some idiomatic CSS practices.

 .list {
  list-style: none;
}
.list-container {
  cursor: pointer;
  font-size: 3.5rem;
  height: 0;
  list-style: none;
  position: relative;
  text-align: center;
  width: 300px;
}
.list-container:not(:first-child) {
  margin-top: 10px;
}
.list-container .list-item {
  background-color: #D3D3D3;
  left: 0;
  padding: 2rem 0;
  position: absolute;
  top: 0;
  transition: all 0.6s ease-out;
  width: 100%;
}
.add-btn {
  background-color: transparent;
  border: 1px solid black;
  cursor: pointer;
  font-size: 2.5rem;
  margin-top: 10px;
  padding: 2rem 0;
  text-align: center;
  width: 300px;
}
Copy after login

How to deal with spacing

First, we use margin-top to create vertical space between elements in the stack. There is no margin at the bottom so that other list items can fill the gap created by the removed list items. This way, even if we set the container height to zero, it still has margins at the bottom. The extra space is created between the list items directly below the deleted list items. And the same list item should be moved upward in response to the deleted list item's container height is zero. And because this extra space further expands the vertical gap between list items, we use margin-top to prevent this from happening.

But we only do this if the project container in question is not the first item in the list. This is why we use :not(:first-child) - it locates everything except the first container (enable selector). We do this because we don't want the first list item to push down from the top edge of the list. We just want this to happen on all items after that, because they are directly below another list item, and the first list item is not.

Now, this is unlikely to make total sense, as we are not currently setting the height of any element to zero. But we will do this later, in order to get the vertical spacing between list elements correct, we need to set the margins like we did.

Instructions on positioning

Another thing worth pointing out is the fact that .list-item elements are nested in their parent .list-container .list-container , they are set to have absolute positions, meaning that they are positioned outside the DOM relative to their relative positioning. We do this to make .list-item element float upward when removed, while moving other .list-item elements and filling the gap left by removing this .list-item element. When this happens, .list-container element (not absolutely positioned, so affected by the DOM) collapses its height, allowing other .list-container elements to fill their position, while .list-item element (located in absolute fashion) floats upwards, but does not affect the structure of the list, as it is not affected by the DOM.

Processing height

Unfortunately, we haven't done enough to get a suitable list where individual list items are stacked one by one. Instead, all we can see at the moment is a .list-item , which represents all list items stacked in the same position. This is because, although .list-item element may have a certain height through its padding attribute, their parent element does not, but is a height of zero. This means that there is nothing in the DOM that actually separates these elements from each other, because to do this we need our .list-container elements to have a certain height because unlike their child elements, they are affected by the DOM.

In order for our list containers to exactly match the height of their child elements, we need to use JavaScript. Therefore, we store all list items in one variable. Then, we create a function that is called immediately after the script is loaded.

This becomes a function that handles the height of the list container element:

 const listItems = document.querySelectorAll('.list-item');

function calculateHeightOfListContainer(){
};

calculateHeightOfListContainer();
Copy after login

The first thing we do is extract the first .list-item element from the list. We can do this because they are all the same size, so it doesn't matter which element we use. Once we access it, we store its height in pixels through the clientHeight property of the element. After that, we create a new one