swiper
swiper copied to clipboard
Infinite loop with slidesPerView: auto, loopFix() issues
This is a (multiple allowed):
-
[x] bug
-
[ ] enhancement
-
[ ] feature-discussion (RFC)
-
Swiper Version: 4.4.6 (also tested 4.4.x versions and the problem persists)
-
Platform/Target and Browser Versions: all platforms and browsers I've been testing: Windows/Android and Chrome/Opera/Firefox (the most actual versions)
-
Live Link or JSFiddle/Codepen or website with issue: -"big case" codepen -"small/simplified case" codepen
What I did
I use swiper with the following settings (the "big case"):
autoplay: {
delay: 0
},
centeredSlides: true,
coverflowEffect: {
depth: 100,
modifier: 1,
rotate: 5,
stretch: 0
},
effect: 'coverflow',
freeMode: true,
freeModeMomentumBounce: false,
freeModeMomentumRatio: .1,
freeModeMomentumVelocityRatio: .8,
freeModeSticky: true,
grabCursor: true,
loop: true,
loopAdditionalSlides: slidesNum, // slidesNum contains the initial slides number
loopedSlides: slidesNum,
slidesPerView: 'auto',
speed: 20000,
breakpoints: {
360: {
slidesPerView: 1
}
}
+some css styles (I did put them in codepens). After some experiments I've managed to recreate the issue only with these settings (the "small case"):
centeredSlides: true,
loop: true,
loopedSlides: slidesNum,
slidesPerView: 'auto',
I don't know if solving problem for the "small case" will be sufficient for solving the "big case", but for sure it's a good way to start.
Behavior
If I slide to the left, everything seems fine (unless the screen is very big or there are too few slides, i.e. 2), but when I swipe to the right, duplicated elements won't show up on the right side of the slider, which causes a huge empty space after the slides. It's even worse if I swipe very fast (it may cause the whole container to be empty for a moment). Occasionally the slider updates and the duplicates "jump in" to the right place, but it's too soon, so it looks bad and may be confusing for the user.
I could find some similar issues here on GitHub, but none of the presented fixes worked for me. For instance I've been playing with loopedSlides and loopAdditionalSlides options (i.e. I've been trying to put there some huge values like slidesNum, 2 * slidesNum, 10 * slidesNum, 50 or slidesNum - 1, where slidesNum contains the initial slides number). It didn't help.
Moreover I've got impression that changing loopAdditionalSlides value option is doing literally nothing. In the docs it's said that it's the "number of slides that will be cloned after creating of loop", but as far as I could see, number of cloned slides is always equal to the number of original slides - please correct me if I'm wrong. So technically, how does this option work?
The other thing I've tried was translating the slider manually when the very last slide appears on the screen, but then I had problems with smooth movement/free mode momentum (see "Big case").
Attemption to fix this
I've been trying to modify swiper.js script in order to make it work. First of all I've noticed that the method loopFix is responsible for "fixing" the loop (to be more specific it handles the translation of slider if some conditions are met). These conditions look like this:
if (activeIndex < loopedSlides) {
// Fix For Negative Oversliding
} else if ((params.slidesPerView === 'auto' && activeIndex >= loopedSlides * 2) || (activeIndex >= slides.length - loopedSlides)) {
// Fix For Positive Oversliding
}
First of all I replaced the 2 multiplier in the second condition with 1 (to tell the truth I couldn't understand why the 2 is there, as activeIndex >= loopedSlides is the opposite condition to the activeIndex < loopedSlides). Can some good soul explain me why there is 2? Thanks!
Secondly I used the console.log and observed that the first condition for negative oversliding was sometimes fulfilled even though I was swiping to the positive direction, so I decided to fix this by imposing some additional condition here related to the sliding direction. So I got something like this:
var previousIndex = swiper.previousIndex;
var dir = (activeIndex > previousIndex) ? 'right' : 'left';
if (dir === 'left' && activeIndex < loopedSlides) {
// Fix For Negative Oversliding
} else if (dir === 'right' && ((params.slidesPerView === 'auto' && activeIndex >= loopedSlides) || (activeIndex >= slides.length - loopedSlides))) {
// Fix For Positive Oversliding
}
In general this fix may not be sufficient for some RTL settings, but it helped a little bit in my case. Here is an example: "attemption to fix" codepen.
As you can see it may seem to work now, but there are still some problems:
- variable
dirhas got wrong value after first swipe in the opposite direction (for example if we are swiping left and then we swipe right for once, thedirvariable still evaluates to 'left' not to 'right'; it'll have the correct value 'right' only if we swipe right for the second time and further). So the problem still may occur in these kinds of situations. - fast swiping can still cause swiper container to be partially empty; in the worst scenario I could limit the maximum slider speed, but it's not very user friendly
- (hard to reproduce) I'm not sure if it concerns the small case, but for the big case in Opera and Chrome browsers some random swiping may cause an infinite script loop (more precisely the method
swiper.loopFix();in line3038of the swiper.js (ver. 4.4.6) starts to run infinitely, causing the whole slider to freeze).
Ok, I think I fixed the major problems in my case (but it may not be sufficient in general).
-
I modified first condition like this:
if (dir === 'left' && activeIndex < loopedSlides && !(activeIndex === previousIndex - 1 && previousIndex > loopedSlides / 2)) { -
I added an argument "direction" to the loop fix method, which can be "left", "right" or undefined:
function loopFix (direction) { -
I modified the conditions once again, taking the argument into account:
if ((typeof direction === 'undefined' || (typeof direction !== 'undefined' && direction === 'left')) && activeIndex < loopedSlides && !(activeIndex === previousIndex - 1 && previousIndex > loopedSlides / 2)) {
// Fix For Negative Oversliding
} else if ((typeof direction === 'undefined' || (typeof direction !== 'undefined' && direction === 'right')) && (params.slidesPerView === 'auto' && activeIndex >= loopedSlides) || (activeIndex >= slides.length - loopedSlides)) {
// Fix For Positive Oversliding
}
- I passed an argument to this method in following places:
- changed
swiper.loopFix();toswiper.loopFix('right');inslideNextmethod - changed
swiper.loopFix();toswiper.loopFix('left');inslidePrevmethod - changed
swiper.loopFix();toswiper.loopFix('right');in theAutoplayvariable initialization (but in general the argument here should be dependant on initial slider direction) - and the most complicated one; in method
onTouchMoveI have addedvar diff = swiper.isHorizontal() ? diffX : diffY;right before theif (!data.isMoved) {condition and then changed the nextswiper.loopFix();toswiper.loopFix(diff > 0 ? 'left' : 'right');
The last point is done like this, because I couldn't get the correct swipe direction in the loopFix method (as stated in the previous post).
Additionally I commented out this fragment:
if (needsLoopFix) {
swiper.once('transitionEnd', function () {
swiper.loopFix();
});
}
as sometimes it was causing the slider to freeze (the loopFix method was called infinitely, I don't know why).
codepen: https://codepen.io/stasiak/pen/pGvGaq
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Have the same issue:
const swiperInstance = new Swiper(containerRef.current, {
direction: 'horizontal',
slidesPerView: 'auto',
loopedSlides: 3,
loop: true
});
With following config Swiper correctly work to the left left, but not to the right.
UPD:
I think the issue reproducible when width of all the slides (not cloned) less than width of "viewport".
Yes i also experience this issue.
I would like to add that it has definitely something to do with the combined width of the elements and the viewport.
Also, setting centeredSlides to true, everything works but that's not what i want in my case.
Same issue
There is such a problem. Does anyone know a solution?
@nullorone apparently it's based on the amount of slides you are showing , at least for me that was the case.
I had a slider which initially had 6 swiper-slides per view in my config, but one of those divs didn't exist or was empty because it was being handled programatically, so swiper didn't know what to do with that last one which in turn just messed it up, so in that case I had only two ways to go, to occupy the slides, either use centered slides which made the 5 slides I had , take up the width of 6 slides by centering everything, filling up that last slide without using centered slides or putting slidesperview to 5 which was the amount of swiper-slide divs that were available at the time.
If that helps at all, awesome.
Had the same issue. Fixed mine by duplicating the array of slides with PHP array_merge(), so that there were more slides in total than slides that was showing in the view. Seemed to work for me!
Same here, I duplicated foreach loop in html/php and it works. It's like manual clone of elements. It's a workaround, but it's easiest solution for now I think.
Is there an easy workaround for this without duplicating PHP loops or modifying source code?
hmmm. in my case, loopFix event didn't fired on swiper.slideTo()
when i dragged the slides, loopFix is fired well.
I would like to add that it has definitely something to do with the combined width of the elements and the viewport.
Also, setting centeredSlides to true, everything works but that's not what i want in my case.
Working for me!
I've created the following workaround to this problem. It follows the same principle of duplicating the slides but does it with JavaScript before the initialization instead of server-side.
on: {
beforeInit: function(swiper){
if(swiper.$el[0].querySelectorAll('.swiper-slide').length == 0) return;
let getTotalWidth = function(swiper){
let width = 0;
swiper.$el[0].querySelectorAll('.swiper-slide').forEach(function(element){
width += element.offsetWidth;
});
return width;
}
while(getTotalWidth(swiper) < window.innerWidth){
swiper.$el[0].querySelectorAll('.swiper-slide').forEach(function(element){
let clone = element.cloneNode(true);
swiper.$el[0].querySelectorAll('.swiper-wrapper')[0].appendChild(clone);
});
}
}
}
I fixed this by not initializing the Swiper if the width of the slides are smaller than the wrapper. Basically, there's no need for a slider for 2 images. Of course, it's not always the case.
Basically I return the needed configuration values like this:
function shouldBeEnabled(carousel) {
const slidesCount = carousel.querySelectorAll('.swiper-slide').length;
let settings;
const wrapperWidth = carousel.querySelector('.js-swiper-wrapper').offsetWidth;
const slidesWidth = [...carousel.querySelectorAll('.swiper-slide')]
.map((el) => el.offsetWidth)
.reduce((total, width) => total + width);
if (slidesWidth < wrapperWidth) {
settings = {
loop: false,
slidesPerView: slidesCount,
enabled: false,
};
return settings;
}
return null;
}
I'm appending this to the config object like this:
const config = {
loop: true,
centeredSlides: true,
slidesPerView: 1,
...shouldBeEnabled(carousel)
};
new Swiper(`#blabla`, config)
Maybe this helps where this is accepted as a workaround.
Been facing this issue as well, none of the workarounds above seem to work...
Weirdly enough, like someone said above, it only happens when clicking the slide, which calls the slideTo function. When I swipe it works just fine.
In my case I'm using react with a media query hook to change the max-width of the slide. Like so:

Been facing this issue as well, none of the workarounds above seem to work...
Weirdly enough, like someone said above, it only happens when clicking the slide, which calls the slideTo function. When I swipe it works just fine.
In my case I'm using react with a media query hook to change the max-width of the slide. Like so:
Try to use width or max-width auto (can't remember exactly) for the swiper slide to overwrite the plugin width calculation in CSS. You need to use "important!".
@LeheneTudor Doesn't work in my case since I'm rendering images without any intrinsic size.
I'm also not setting any widths in the ImageSlide comp. It's all very simple...


centeredSlidesBounds = true and centeredSlides = true works for me and fixes the problem when I swipe right and my slides exceed slides > 3.
Swiper v9 comes with fully reworked and now different loop functionality. If you have similar issues in Swiper 9, open a new issue with a CodeSandbox showing the issue.