Swiper: In loop mode, duplicate slides are not being updated when the contents of the original slides change

Created on 17 May 2018  路  19Comments  路  Source: nolimits4web/swiper

This is a (multiple allowed):

  • [x] bug
  • [ ] enhancement
  • [ ] feature-discussion (RFC)
  • Swiper Version: 4.2.6
  • Platform/Target and Browser Versions: All browsers, but tested on macOS, CHROME
  • Live Link or JSFiddle/Codepen or website with issue: https://jsfiddle.net/knankv59/2/

What you did

Swiper config is at the bottom of the Javascript window in the jsfiddle (loop mode, loopedSlides set to 3, slidesPerView set to 1)

Click the right navigation arrow twice to swipe forward to slide 2.
Click the button above the slideshow to change the content of slide 2, which now reads "New content!"
Click the right navigation arrow six more times, going to the end of the slides and looping back around to slide 2.

Expected Behavior

Slide 2's text should still say "New content!"

Actual Behavior

Slide 2's text is still just the original "2".

_
Is there any way to refresh specific duplicate slides automatically when their original slide's content changes?

Most helpful comment

I was struggling with this behavior as well and since I could not find any solution out of the box for my problem I ended up with a mix of hacks that worked perfectly to me. In my case some slides have a counter and also some buttons, both were not working on duplicated slides. So I used the solution proposed by @golee to copy the dom at the moment of slide transition start (slideChangeTransitionStart) and then on (slideChangeTransitionEnd) I slide it to the realIndex but with speed 0 so it's not noticed by the user.

slideChangeTransitionStart() {        
            let $wrapperEl = this.swiper.$wrapperEl;
            let params = this.swiper.params;
            $wrapperEl.children(('.' + (params.slideClass) + '.' + (params.slideDuplicateClass)))
                .each(function () {
                    let idx = this.getAttribute('data-swiper-slide-index');
                    this.innerHTML = $wrapperEl.children('.' + params.slideClass + '[data-swiper-slide-index="' + idx + '"]:not(.' + params.slideDuplicateClass + ')').html();
                });
    }

slideChangeTransitionEnd() {
            this.swiper.slideToLoop(this.swiper.realIndex, 0, false);
    }

All 19 comments

Same issue here. Swiper does not update duplicate slides when the original has changed. Any workarounds would be highly appreciated.

@jhowsick One way is manually redoing the loop by calling methods on the swiper instance itself.

const swiper = this.swiper;
swiper.loopDestroy();
swiper.loopCreate();

This is hacky though. Hopefully an official fix will happen.

I had the same problem

me too

It is not supported and won't be, it should be done manually, for example like @brandonce described

How about something similar with renderExternal() ?.. Sync React or Vue with loop mode is so painful.

I currently use the method that just update html of dupslides

updateDuplicateSlides = function() {
  var swiper = this;
  var $wrapperEl = swiper.$wrapperEl;
  var params = swiper.params;
  var slides = swiper.slides;
  $wrapperEl.children(("." + (params.slideClass) + "." + (params.slideDuplicateClass)))
  .each(function() {
    var idx = this.getAttribute('data-swiper-slide-index');
    this.innerHTML = $wrapperEl.children('.'+params.slideClass+'[data-swiper-slide-index="'+idx+'"]:not(.'+params.slideDuplicateClass+')').html();
})

Our swiper, here to note the onTransitionStart function of passing our function to trade the duplicates.

new Swiper( '.swiper-container', {
    slidesPerView: 1.25,
    spaceBetween: 15,
    centeredSlides: true,
    parallax: true,
    onTransitionStart: handleDuplicates.bind( this ) // To handle the duplicates
} )

Your slides with events we set:

let videos: [].slice.call( document.querySelectorAll( '.swiper-slide .videobox video' ) );

videos.forEach( function ( item, index ) {
    item.addEventListener( 'touchstart', playVideo.bind( this ) );
}.bind( this ) );

We handle the duplicates on transition start with this function handleDuplicates()

/**
 * Update, change or do what you want here with the duplicates
 */
handleDuplicates() {

    let swiperDuplicatesWithoutEvents = [].slice.call( document.querySelectorAll( '.swiper-slide-duplicate .videobox video' ) );

    // Remove olds
    swiperDuplicatesWithoutEvents.forEach( function ( item, index ) {
        item.removeEventListener( 'touchstart', playVideo.bind( this ) );
    }.bind( this ) );

    // Add news
    swiperDuplicatesWithoutEvents.forEach( function ( item, index ) {
        item.addEventListener( 'touchstart', playVideo.bind( this ) );
    }.bind( this ) );
}

I was struggling with this behavior as well and since I could not find any solution out of the box for my problem I ended up with a mix of hacks that worked perfectly to me. In my case some slides have a counter and also some buttons, both were not working on duplicated slides. So I used the solution proposed by @golee to copy the dom at the moment of slide transition start (slideChangeTransitionStart) and then on (slideChangeTransitionEnd) I slide it to the realIndex but with speed 0 so it's not noticed by the user.

slideChangeTransitionStart() {        
            let $wrapperEl = this.swiper.$wrapperEl;
            let params = this.swiper.params;
            $wrapperEl.children(('.' + (params.slideClass) + '.' + (params.slideDuplicateClass)))
                .each(function () {
                    let idx = this.getAttribute('data-swiper-slide-index');
                    this.innerHTML = $wrapperEl.children('.' + params.slideClass + '[data-swiper-slide-index="' + idx + '"]:not(.' + params.slideDuplicateClass + ')').html();
                });
    }

slideChangeTransitionEnd() {
            this.swiper.slideToLoop(this.swiper.realIndex, 0, false);
    }

@nolimits4web I follow @brandonce described, then found if I have 2 slides in swiper, I only can change the first slide value and can't change the second one. swiper.loopDestroy() will rollback the value of second slide.

//this will update the duplicates only once if you slides update

this.mySwiper.once('transitionStart',function updateDuplicates() {
this.loopDestroy()
this.loopCreate()
})

//this will update the duplicates only once if you slides update

this.mySwiper.once('transitionStart',function updateDuplicates() {
this.loopDestroy()
this.loopCreate()
})

Hello, I'm having the same problem and can't fix it. Is possible for you to give me a real example, probably i'm pasting your code in a wrong place.

Thanks a lot.

const swiper = this.swiper;
swiper.loopDestroy();
swiper.loopCreate();

@JoseCigarro you should run these methods whenever the original slides change.
this.swiper points at your Swiper instance. Here is an example of how you can access it through Swiper events or swiper's HTMLElement - http://idangero.us/swiper/api/#initialize

I was struggling with this behavior as well and since I could not find any solution out of the box for my problem I ended up with a mix of hacks that worked perfectly to me. In my case some slides have a counter and also some buttons, both were not working on duplicated slides. So I used the solution proposed by @golee to copy the dom at the moment of slide transition start (slideChangeTransitionStart) and then on (slideChangeTransitionEnd) I slide it to the realIndex but with speed 0 so it's not noticed by the user.

slideChangeTransitionStart() {        
            let $wrapperEl = this.swiper.$wrapperEl;
            let params = this.swiper.params;
            $wrapperEl.children(('.' + (params.slideClass) + '.' + (params.slideDuplicateClass)))
                .each(function () {
                    let idx = this.getAttribute('data-swiper-slide-index');
                    this.innerHTML = $wrapperEl.children('.' + params.slideClass + '[data-swiper-slide-index="' + idx + '"]:not(.' + params.slideDuplicateClass + ')').html();
                });
    }

slideChangeTransitionEnd() {
            this.swiper.slideToLoop(this.swiper.realIndex, 0, false);
    }

This one is super and awesome fix. You saved my day. Thank you very much.

I use Swiper in Vue with a loop and need change slides data. Of course, the loop doesn't change duplicates data so I need to use this.loopDestroy () and this.loopCreate () but ... when to run them? I came to the point that I have to run them after creating DOM Swiper but how to detect this? Because I use Vue I can fire it on "update", but without it, I don't have any good idea. Of course, I can use interval and check something in DOM but it sucks in my opinion.

hmm this fix worked for me
before adding a slider destroy the loop
swiper.loopDestroy();
Add the slider setTimeout for a sec and update the swiper and create the loop again
setTimeout({
directiveRef.update or swiper.update()
swiper.loopCreate();}, 1000);

the loop doesnt work properly on slide update so manually updating it! hope it works for you as well!

Thank you so much @victorcamargos for the perfect solution! Here is how I used it on my code if anyone needs it:

let productOptions = {
    slidesPerView: 1,
    autoHeight: true,
    loop: true,


    on: {

        slideChangeTransitionStart: function(swiper) {
            let $wrapperEl = swiper.$wrapperEl;
            let params = swiper.params;
            $wrapperEl.children(('.' + (params.slideClass) + '.' + (params.slideDuplicateClass)))
                .each(function() {
                    let idx = this.getAttribute('data-swiper-slide-index');
                    this.innerHTML = $wrapperEl.children('.' + params.slideClass + '[data-swiper-slide-index="' + idx + '"]:not(.' + params.slideDuplicateClass + ')').html();
                });
        },

        slideChangeTransitionEnd: function(swiper) {
            swiper.slideToLoop(swiper.realIndex, 0, false);
        }


    }

}

const product = new Swiper(".swiper-image-container", productOptions);

I am using Vue, not sure if this helps to someone but in my case I just destroyed the slider and after 100 milliseconds initialized it again:

this.swiper.destroy()
_.delay(() => {
    this.swiper = new Swiper(this.$refs.swiper, this.swiperOptions)
 }, 100)

Quick edit: why the delay? The initialization after destroying the instance does not work without it. This can be of course changed by a regular setTimeout().

setTimeout(()=>{
            that.swiper.loopDestroy();
            that.swiper.loopCreate();
            console.log('reborn!')
          })

this is just alright and clean !

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sackIndian picture sackIndian  路  3Comments

leone510es picture leone510es  路  3Comments

magic-77 picture magic-77  路  3Comments

danielcpereira11 picture danielcpereira11  路  4Comments

cristianfierro picture cristianfierro  路  4Comments