javascripthtmlcsscss-animations

CSS Animation to go only forward and stop. Currently it goes back and forth


I am trying to make this work so that only one pseudo-element shows "progress" if the item is highlighted (i.e., has the highlighted class).

Currently, the animation moves forward correctly, but once it reaches the end, the animation reverses.

Each item should be highlighted for 8 seconds, during which the progress bar should fill from top to bottom. Afterward, the highlight should move to the next item. This behavior should loop through all items continuously.

const items = document.querySelectorAll('.enhanced-content__animated-list-item');
  let index = 0;
  const totalItems = items.length;

  const highlightItem = () => {
    // Remove 'highlight' and 'progress' classes from all items
    items.forEach(item => {
      item.classList.remove('highlight', 'progress');
    });

    // Get the current item
    const currentItem = items[index];

    // Add 'highlight' and 'progress' classes to the current item
    currentItem.classList.add('highlight', 'progress');

    // After 8 seconds, move to the next item
    setTimeout(() => {
      index = (index + 1) % totalItems;
      highlightItem();
    }, 8000);
  };

  // Start the cycle
  highlightItem();
.enhanced-content__container {
    background: #fff; /* Replace $COLOR_BACKGROUND_WHITE */
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    left: 24px; /* Replace $SPACING_L */
    padding-block: 24px; /* Replace $SPACING_L */
    padding-inline: 24px; /* Replace $SPACING_L */
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 60%;
    margin-top: 5rem;
  }

  .enhanced-content__animated-list-item {
    flex-direction: column;
    margin-bottom: 20px;
    padding: 1rem;
    position: relative;
    text-align: left;
  }

  .enhanced-content__animated-list-item .enhanced-content__animated-list-heading,
  .enhanced-content__animated-list-item .enhanced-content__animated-list-text {
    color: #e4e4e4;
  }

  .enhanced-content__animated-list-item.highlight .enhanced-content__animated-list-heading,
  .enhanced-content__animated-list-item.highlight .enhanced-content__animated-list-text {
    color: black;
  }

  .enhanced-content__animated-list-item::before {
    background-color: #e4e4e4;
    content: '';
    height: calc(100% - 20px);
    left: 0;
    margin-top: 10px;
    position: absolute;
    top: 0;
    width: 1.5px;
  }

  .enhanced-content__animated-list-item::after {
    background-color: #007bff;
    content: '';
    height: 0;
    left: 0;
    position: absolute;
    top: 0;
    transition: height 8s linear;
    width: 1.5px;
  }

  .enhanced-content__animated-list-item.progress::after {
    height: calc(100% - 20px);
  }
}
<div class="enhanced-content">
  <div class="enhanced-content__container container">
    <h2 class="h5">heading</h2>

    <!-- Content for Desktop -->
    <div class="enhanced-content__desktop">
      <div class="enhanced-content__animated-list">
          <div class="enhanced-content__animated-list-item">
            <h3 class="enhanced-content__animated-list-heading h5">
              title 1
            </h3>
            <p class="enhanced-content__animated-list-text body-1">
            content one
            </p>
          </div>
          <div class="enhanced-content__animated-list-item">
            <h3 class="enhanced-content__animated-list-heading h5">
              title 2
            </h3>
            <p class="enhanced-content__animated-list-text body-1">
            content two
            </p>
          </div>
          <div class="enhanced-content__animated-list-item">
            <h3 class="enhanced-content__animated-list-heading h5">
              title 3
            </h3>
            <p class="enhanced-content__animated-list-text body-1">
            content three
            </p>
          </div>
          <div class="enhanced-content__animated-list-item">
            <h3 class="enhanced-content__animated-list-heading h5">
              title 4
            </h3>
            <p class="enhanced-content__animated-list-text body-1">
            content four
            </p>
          </div>
      </div>
    </div>
    </div>
  </div>


Solution

  • The issue you're running into is due to the transition behavior: it's reversible, so when you remove the class that triggers the changes, it reverts the state at the same (slooooow) speed.

    I’d recommend using animation instead. It has a forwards property, so it stays in the changed state and doesn’t reverse itself when the progress class is removed.

    this is basically all changes you need:

    @keyframes progress-bar {
      0% {
        height: 0;
      }
      98% {
        height: calc(100% - 20px);    
      }
      100% {
        height: 0;
      }
    }
      .enhanced-content__animated-list-item.highlight::after {
        animation: progress-bar 8s linear forwards;
    }
    

    this is your code snippet with that css applied:

    const items = document.querySelectorAll('.enhanced-content__animated-list-item');
      let index = 0;
      const totalItems = items.length;
    
      const highlightItem = () => {
        // Remove 'highlight' and 'progress' classes from all items
        items.forEach(item => {
          item.classList.remove('highlight', 'progress');
        });
    
        // Get the current item
        const currentItem = items[index];
    
        // Add 'highlight' and 'progress' classes to the current item
        currentItem.classList.add('highlight', 'progress');
    
        // After 8 seconds, move to the next item
        setTimeout(() => {
          index = (index + 1) % totalItems;
          highlightItem();
        }, 8000);
      };
    
      // Start the cycle
      highlightItem();
    .enhanced-content__container {
        background: #fff; /* Replace $COLOR_BACKGROUND_WHITE */
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        left: 24px; /* Replace $SPACING_L */
        padding-block: 24px; /* Replace $SPACING_L */
        padding-inline: 24px; /* Replace $SPACING_L */
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        width: 60%;
        margin-top: 5rem;
      }
    
      .enhanced-content__animated-list-item {
        flex-direction: column;
        margin-bottom: 20px;
        padding: 1rem;
        position: relative;
        text-align: left;
      }
    
      .enhanced-content__animated-list-item .enhanced-content__animated-list-heading,
      .enhanced-content__animated-list-item .enhanced-content__animated-list-text {
        color: #e4e4e4;
      }
    
      .enhanced-content__animated-list-item.highlight .enhanced-content__animated-list-heading,
      .enhanced-content__animated-list-item.highlight .enhanced-content__animated-list-text {
        color: black;
      }
    
      .enhanced-content__animated-list-item::before {
        background-color: #e4e4e4;
        content: '';
        height: calc(100% - 20px);
        left: 0;
        margin-top: 10px;
        position: absolute;
        top: 0;
        width: 1.5px;
      }
    
      .enhanced-content__animated-list-item::after {
        background-color: #007bff;
        content: '';
        height: 0;
        left: 0;
        position: absolute;
        top: 0;
        width: 1.5px;
      }
    
    /* Animation for progress bar filling */
    @keyframes progress-bar {
      0% {
        height: 0;
      }
      98% {
        height: calc(100% - 20px);    
      }
      100% {
        height: 0;
      }
    }
    
      .enhanced-content__animated-list-item.highlight::after {
        animation: progress-bar 8s linear forwards;
    }
    <div class="enhanced-content">
      <div class="enhanced-content__container container">
        <h2 class="h5">heading</h2>
    
        <!-- Content for Desktop -->
        <div class="enhanced-content__desktop">
          <div class="enhanced-content__animated-list">
              <div class="enhanced-content__animated-list-item">
                <h3 class="enhanced-content__animated-list-heading h5">
                  title 1
                </h3>
                <p class="enhanced-content__animated-list-text body-1">
                content one
                </p>
              </div>
              <div class="enhanced-content__animated-list-item">
                <h3 class="enhanced-content__animated-list-heading h5">
                  title 2
                </h3>
                <p class="enhanced-content__animated-list-text body-1">
                content two
                </p>
              </div>
              <div class="enhanced-content__animated-list-item">
                <h3 class="enhanced-content__animated-list-heading h5">
                  title 3
                </h3>
                <p class="enhanced-content__animated-list-text body-1">
                content three
                </p>
              </div>
              <div class="enhanced-content__animated-list-item">
                <h3 class="enhanced-content__animated-list-heading h5">
                  title 4
                </h3>
                <p class="enhanced-content__animated-list-text body-1">
                content four
                </p>
              </div>
          </div>
        </div>
        </div>
      </div>