Search code examples
javascripthtmlcsscss-animations

How can I animate sidebar items sliding up when an item is deleted?


I am trying to create a sidebar with a list of items. When an item is deleted, I want the remaining items to slide up smoothly to fill the space left by the deleted item. How can I achieve this?

This is my code:

document.addEventListener("DOMContentLoaded", function () {
   const deleteButtons = document.querySelectorAll(".del");

   deleteButtons.forEach((button) => {
      button.addEventListener("click", function () {
         const item = this.parentElement;
         item.classList.add("deleting");
         item.addEventListener("transitionend", function () {
            item.remove();
         });
      });
   });
});
.sidebar {
   width: 250px;
   background-color: #f0f0f0;
   padding: 10px;
   border-right: 1px solid #ccc;
   border-radius: 10px;
}

.item-list {
   list-style-type: none;
   padding: 0;
   margin: 0;
}

.item {
   background-color: #fff;
   margin: 5px 0;
   padding: 10px;
   border: 1px solid #ccc;
   display: flex;
   justify-content: space-between;
   align-items: center;
   border-radius: 10px;
   transition: transform 0.3s ease, opacity 0.3s ease;
}

.item.deleting {
   transform: translateY(-100%);
   opacity: 0;
   border: none;
}

.del {
   background-color: #ff6666;
   border: none;
   color: white;
   padding: 5px 10px;
   cursor: pointer;
   border-radius: 9999px;
   transition: background-color 0.3s;
}

.del:hover {
   background-color: #ff3333;
}
<div class="sidebar">
   <ul class="item-list">
      <li class="item">item 1 <button class="del">Delete</button></li>
      <li class="item">item 2 <button class="del">Delete</button></li>
      <li class="item">item 3 <button class="del">Delete</button></li>
      <li class="item">item 4 <button class="del">Delete</button></li>
      <li class="item">item 5 <button class="del">Delete</button></li>
      <li class="item">item 6 <button class="del">Delete</button></li>
      <li class="item">item 7 <button class="del">Delete</button></li>
      <li class="item">item 8 <button class="del">Delete</button></li>
      <li class="item">item 9 <button class="del">Delete</button></li>
      <li class="item">item 10 <button class="del">Delete</button></li>
   </ul>
</div>

While the deleting item moves up, the other items don't immediately move to fill its space until the item is deleted. How can I make sure that the other items move up to fill its space while the item is moving up?


Solution

  • transform: translateY leaves the space the element would normally occupy, reserved - so if you want to do it using that, you would have to apply the same to all following items at the same time.

    Easier to do, if you manipulate the item's margin-top instead. Only you won't be able to use -100% then (because margins in percent, are relative to the width of the element) - but you can easily dynamically set the margin, according to the item's height.

    document.addEventListener("DOMContentLoaded", function () {
       const deleteButtons = document.querySelectorAll(".del");
    
       deleteButtons.forEach((button) => {
          button.addEventListener("click", function () {
             const item = this.parentElement;
             item.classList.add("deleting");
             /* set negative margin-top dynamically,
                corresponding to item height */
             item.style.marginTop = "-" + item.offsetHeight + "px";
             item.addEventListener("transitionend", function () {
                item.remove();
             });
          });
       });
    });
    .sidebar {
       width: 250px;
       background-color: #f0f0f0;
       padding: 10px;
       border-right: 1px solid #ccc;
       border-radius: 10px;
    }
    
    .item-list {
       list-style-type: none;
       padding: 0;
       margin: 0;
    }
    
    .item {
       background-color: #fff;
       margin: 5px 0;
       padding: 10px;
       border: 1px solid #ccc;
       display: flex;
       justify-content: space-between;
       align-items: center;
       border-radius: 10px;
       /* transition margin instead of translate */
       transition: margin 0.3s ease, opacity 0.3s ease;
    }
    
    .item.deleting {
       /* removed translate here; */
       opacity: 0;
       border: none;
    }
    
    .del {
       background-color: #ff6666;
       border: none;
       color: white;
       padding: 5px 10px;
       cursor: pointer;
       border-radius: 9999px;
       transition: background-color 0.3s;
    }
    
    .del:hover {
       background-color: #ff3333;
    }
    <div class="sidebar">
       <ul class="item-list">
          <li class="item">item 1 <button class="del">Delete</button></li>
          <li class="item">item 2 <button class="del">Delete</button></li>
          <li class="item">item 3 <button class="del">Delete</button></li>
          <li class="item">item 4 <button class="del">Delete</button></li>
          <li class="item">item 5 <button class="del">Delete</button></li>
          <li class="item">item 6 <button class="del">Delete</button></li>
          <li class="item">item 7 <button class="del">Delete</button></li>
          <li class="item">item 8 <button class="del">Delete</button></li>
          <li class="item">item 9 <button class="del">Delete</button></li>
          <li class="item">item 10 <button class="del">Delete</button></li>
       </ul>
    </div>