Swiper pagination bullet: styling for visited slides' pagination bullet as well

39 views Asked by At

I have created this Swiper slider with pagination bullets that have a progress bar pagination. It changes color for the bullet (to #000) on active slides. Is there a way for the bullets for the slide that was visited has their colored changed (to #000) as well - so only the unvisited slide's bullet with the grey #DDD background?

Here's my code:

var mySwiper = new Swiper('.swiper-container', {
        loop: true,
        slidesPerView: 1,        
        autoplay: {
            delay: 5000,
        }, 
        effect: 'fade',
        fadeEffect: {
          
            crossFade: true
        },
        pagination: {
            el: '.swiper-pagination',
            clickable: 'true',
            type: 'bullets',
            renderBullet: function (index, className) {
                return '<span class="' + className + '">' + '<i class="progress-bar-bg"></i>' + '<b class="progress-bar-cover"></b>'  + '</span>';
              },
        },
})
:root {
    --swiper-pagination-bullet-border-radius: 0;
    --swiper-pagination-bullet-width: 40px;
    --swiper-pagination-bullet-height: 2px;
}


body {
  font-family: Helvetica;
  color: #000;
}

.swiper-container {
  width: 100%; height: 100vh;
}

.swiper-wrapper {
  width: 100%; height: 100%; 
}

.swiper-slide {
  font-size: 100px; text-align: center; 
  line-height:100vh;
}

.swiper-pagination-bullet {
    position: relative;
    height: auto;
    opacity: 1;
    margin-right: 20px;
    background-color: transparent;
  
    .progress-bar-bg {
        position: absolute;
        bottom: 0;
        left: 0;
        z-index: 1;
        width: 100%;
        height: 2px;
        background-color: #DDD;
    }
    .progress-bar-cover {
        position: absolute;
        bottom: 0;
        left:  0;
        z-index: 2;
        width: 0%;
        height: 2px;
        background-color: #000;
    }
}

.swiper-pagination-bullet-active {
    background-color: transparent;
    b {  
        animation-name: countingBar;
        animation-duration: 3s;
        animation-timing-function: ease-in;
        animation-iteration-count: 1;
        animation-direction: alternate ;
        animation-fill-mode:forwards;
    }
}

@keyframes countingBar {
    0% {width: 0;}
    100% {width:100%;}
}
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>

<!-- Slider main container -->
<div class="swiper-container">
    <!-- Additional required wrapper -->
    <div class="swiper-wrapper">
        <!-- Slides -->
        <div class="swiper-slide">Slide 1</div>
        <div class="swiper-slide">Slide 2</div>
        <div class="swiper-slide">Slide 3</div>
        ...
    </div>
    <!-- If we need pagination -->
    <div class="swiper-pagination"></div>
</div>

Any pointers would be an immense help. Thank you so much.

In JavaScript, I tried to add an event listener to visited slide through click and added a 'visited-slide' class before. However, it requires a click but won't automatically updating the bullet's color as the slide animation goes on.

1

There are 1 answers

1
FiddlingAway On

You can define the event handler within your initialization. For example, with your code:

var mySwiper = new Swiper('.swiper-container', {
  loop: true,
  slidesPerView: 1,        
  autoplay: {
    delay: 5000,
  }, 
  effect: 'fade',
  fadeEffect: {

    crossFade: true
  },
  pagination: {
    el: '.swiper-pagination',
    clickable: 'true',
    type: 'bullets',
    renderBullet: function (index, className) {
      return '<span class="' + className + '">' + '<i class="progress-bar-bg"></i>' + '<b class="progress-bar-cover"></b>'  + '</span>';
    },
  },
  /* this is new */
  on: {
    'slideChange': function() {
      const previousIndex = this.previousIndex;
      //const currentIndex = this.activeIndex;
      
      const bullets = document.querySelectorAll('.swiper-pagination-bullet');
      if(bullets && bullets.length > 0) {
        bullets[previousIndex].classList.add("visited");
      }
    }  
  }
  /* */
})
:root {
    --swiper-pagination-bullet-border-radius: 0;
    --swiper-pagination-bullet-width: 40px;
    --swiper-pagination-bullet-height: 2px;
}


body {
  font-family: Helvetica;
  color: #000;
}

.swiper-container {
  width: 100%; height: 100vh;
}

.swiper-wrapper {
  width: 100%; height: 100%; 
}

.swiper-slide {
  font-size: 100px; text-align: center; 
  line-height:100vh;
}

.swiper-pagination-bullet {
    position: relative;
    height: auto;
    opacity: 1;
    margin-right: 20px;
    background-color: transparent;
  
    .progress-bar-bg {
        position: absolute;
        bottom: 0;
        left: 0;
        z-index: 1;
        width: 100%;
        height: 2px;
        background-color: #DDD;
    }
    .progress-bar-cover {
        position: absolute;
        bottom: 0;
        left:  0;
        z-index: 2;
        width: 0%;
        height: 2px;
        background-color: #000;
    }
}

.swiper-pagination-bullet-active {
    background-color: transparent;
    b {  
        animation-name: countingBar;
        animation-duration: 3s;
        animation-timing-function: ease-in;
        animation-iteration-count: 1;
        animation-direction: alternate ;
        animation-fill-mode:forwards;
    }
}

@keyframes countingBar {
    0% {width: 0;}
    100% {width:100%;}
}
/* this is new */
.swiper-pagination-bullet.visited > i {
  background-color: #000;
}
/* */
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>

<!-- Slider main container -->
<div class="swiper-container">
    <!-- Additional required wrapper -->
    <div class="swiper-wrapper">
        <!-- Slides -->
        <div class="swiper-slide">Slide 1</div>
        <div class="swiper-slide">Slide 2</div>
        <div class="swiper-slide">Slide 3</div>
        ...
    </div>
    <!-- If we need pagination -->
    <div class="swiper-pagination"></div>
</div>


First, I used this CSS to mark the visited slides:

.swiper-pagination-bullet.visited > i {
  background-color: #DDD;
}

Next, I added an event handler to do something once the slide changes:

var mySwiper = new Swiper('.swiper-container', {
  /* ... other options ... */
  on: {
    'slideChange': function() {
      const previousIndex = this.previousIndex;
      // const currentIndex = this.activeIndex;
      
      const bullets = document.querySelectorAll('.swiper-pagination-bullet');
      if(bullets && bullets.length > 0) {
        bullets[previousIndex].classList.add("visited");
      }
    }  
  }
})

From the official documentation regarding slideChange event:

Event will be fired when currently active slide is changed

which is what we want. The good thing about this is that we can capture both the previous slide, and the newly activated slide (btw, we don't need this right now, but I left it in the comments in case you need to use it for something else later on), or, rather, we can capture their indices.

Since we can know what the previous slide was, and since the number of the bullets matches the number of the slides, we can query the DOM for the bullets, and change the background for the one whose index matches the index of our previous slide.