Open maxymczech opened 3 years ago
I have the same issue and seems like the problem is with the loop (at least in my case). The layout shift is caused by the slides added before and after your slides on initialisation.
As per the Swiper API docs: Also, because of nature of how the loop mode works, it will add duplicated slides
.
I can also confirm that CLS is caused by loop. Maybe as a solution for a future releases would be adding an option to manually add placeholders for duplicated slides? Before initialization the element would have the width/height set? So it won't cause this problem? Just thinking out loud
I can also confirm that CLS is caused by loop
It actually did not occur to me to try turning loop off. Yes, CLS is 0 with option loop: false
.
If I am not mistaken the issue CLS is when you attempt to click on an item and it moves away. Drastic height changes can probably trigger it, which is what happens with Swiper.
So, with Swiper, before it loads usually all images are stacked on top of each other.
Once it's loaded they are all stacked side by side, causing a sudden abrupt change on layout.
Setting a max height with the size of the first image should fix it, but i haven't tried it yet. If anyone can confirm please let me know, I'll give it a try this weekend.
On Thu, Jan 14, 2021, 09:34 Maksym Shcherban notifications@github.com wrote:
I can also confirm that CLS is caused by loop
It actually did not occur to me to try turning loop off. Yes, CLS is 0 with option loop: false.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/nolimits4web/swiper/issues/4076#issuecomment-760168226, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC6UQSJZ57Q5VNDGTM7QS23SZ3QDPANCNFSM4VHANYSA .
@dpw1 I checked my carousel and it has max height, so the issue is not caused by that. Also CLS is not only when you try to click on the item and it moves away (ref: https://web.dev/cls/)
Here is an example: https://swiperjs.com/demos/#loop_mode_infinite_loop
I think that the CLS happens when Swiper adds an extra slide in the swiper-wrapper
(see example above). Initially swiper-wrapper
has 10 slides. Once Swiper initialises it adds one before the first child and one after the last child.
Adding one after the last child is not an issue. The problem is that it adds one before the first one. So the first slide shifts, basically the first becomes the second.
When it adds an extra slide it doesn't actually cause a visual disruption, does it?
quoting from the link you have referred to:
"A layout shift occurs any time a visible element changes its position from one rendered frame to the next."
If the extra slides are off screen I'm unsure whether it could be the cause
On Thu, Jan 14, 2021, 10:05 Norbert Torok notifications@github.com wrote:
@dpw1 https://github.com/dpw1 I checked my carousel and it has max height, so the issue is not caused by that. Also CLS is not only when you try to click on the item and it moves away (ref: https://web.dev/cls/)
Here is an example: https://swiperjs.com/demos/#loop_mode_infinite_loop
I think that the CLS happens when Swiper adds an extra slide in the swiper-wrapper(see example above). Initially swiper-wrapper has 10 slides. Once Swiper initialises it adds one before the first child and one after the last child.
Adding one after the last child is not an issue. The problem is that it adds one before the first one. So the first slide shifts, basically the first becomes the second.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/nolimits4web/swiper/issues/4076#issuecomment-760184669, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC6UQSJYNTKY6LLFHLW35MLSZ3T2VANCNFSM4VHANYSA .
Yes that is right, it does not causes a visual disruption... But if I run the performance profiling, under the experience summary I can see this:
I just try to find an answer and fix the problem. 🤷♂️
Its because of the dynamic DOM element. I think any cloned element added to tree causes CLS if there is no place reserved for it.
I see. Do you have any potential solutions in mind for it?
Em qui., 14 de jan. de 2021 às 10:31, Ernest F. notifications@github.com escreveu:
Its because of the dynamic DOM element. I think any cloned element added to tree causes CLS if there is no place reserved for it.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/nolimits4web/swiper/issues/4076#issuecomment-760197890, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC6UQSJQH7TT3DNZFMV425LSZ3WZLANCNFSM4VHANYSA .
I'm experiencing the same issue with Swiper and loop mode enabled. With loop mode disabled my CLS score is 0.0006 and with loop mode enabled the CLS score is 0.298. Any site that has a CLS score of above 0.1 is classified as "needs improvement" and any above 0.25 as "poor" https://web.dev/cls/. Google has outlined that they will start using CLS which is part of Core Web Vitals https://web.dev/vitals/ for search engine rankings so it's quite an important issue to solve.
As a temporary solution, turning off loop
worked fine for me.
Yes, this works as a temp solution however turning off loop isn't really the ideal option. I'm using swiper for promotional banners and product display carousels for e-commerce based sites where having the carousel loop is better UX in my opinion. Once again though it's Google creating additional head aches for web devs to keep up with their "ideal" metrics with the underlying threat of getting penalized in search rankings....
Seems like i found a solution
1) set duplicates position to absolute 2) fill their places with ::before/::after pseudo-elems. Use min-width to set width 3) replace duplicate:last-child to the end of wrapper using 'left'
for example, my slides have full-screen width, and i got 4 slides in a loop and this reduced CLS back to 0, although swiper is still working normal:
I try and yes, that also get the problem about CLS when using loop, but not only there have CLS when I combine with slidePerView by 3 or more together, that increase my CLS too.
@K-ETFreeman Unfortunately this solution is breaking the design on my end.
The solution doesn't work for me neither because I can have any number of slides from the back-end, I cannot hardcode it as left: 500%, that won't work for 2 slides or 3 slides, only for 4... 🤔
I can confirm that turning off loop fixes my CLS score. It's not a deal breaker for my site so it's fine.
I'm experiencing the same issue and Core Vitals are needed on my end.
I noticed that without loop
the CLS turns back to 0, but with Autoplay the slider keeps displaying the first slide when it comes to the end, I'm wondering what loop
actually does, though.
@ciromattia loop
allows you to manually loop through slides in a "circular" manner (1 - 2 - 3 - 4 - 5 - 1 - ...)
got the same problem, in my situation helped loop: false
, and had to change one element in renderBullet function
renderBullet: function (i, c) {
return '<span class="' + c + '"><span class="swiper-pagination-txt">' + $(this.slides[i+1]).find('.banners-name').html() + '</span></span>';
}
changed only one element from i+1 to only i $(this.slides[i])
becouse like was said on this forum, it was generating one more layer which produces CLS. Strange thing is that even when loop is false, my slider is still working properly and looping all images... and no CLS at all :)
I used a similiar @K-ETFreeman solution, in my case I have only one slide per view and full page width. This code works for me, don't increment LCP and content is visible also with javascript disabled (if you managed it before)
#{$slides-wrapper-selector}:not([style*='translate3d']) {
max-width: 100%;
#{$duplicated-slide-selector} {
position: absolute !important;
left: -9999px;
}
&::before {
content: '';
min-width: 100%;
position: relative;
left: -9999px;
}
#{$slide-selector} {
transform: translateX(-100%);
}
}
I hope that this workaround can help someone.
Hi all, I think it may help to know this useful resource where you can find CLS metric change log https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/cls.md
I am not sure about your examples but there are more fixes coming with Chrome 89 and 90 soon you can already test in Chrome Canary.
I'm also seeing this issue and unfortunately turning off loop
isn't an option for me. Haven't been able to find any solution that works yet 😞
@kristofferdamborg did you try your site on Chrome Canary 90 to check whether the issue is still happening with loop on?
Guys, I think it is not really the Swiper issue.
Yes, in loop mode it duplicates and adds slides to DOM, it is necessary and must. But! it doesn't create any visual issues and there is no actual layout shift, and what is more important, there is no UI/UX issues for users.
All mentioned workarounds above can do the trick, but all of them doesn't fix anything except "fix this metric", they don't fix the issue, because there is no issue. Even worse, trying to fix this "metric" with these workarounds can just make user experience worse and break the Swiper.
For me, in this case, issue is in the web-vitals library that treats any, literally, any DOM change as CLS, and adds headache to developers for problem which is not exist
@nolimits4web Sorry to chime in, I hope you appreciate the feedback.
I don't know your widget in detail but I can confirm that the CLS metric issue I was able to notice here (that is not related with the web-vitals library but the metric implementation in Chromium) should be fixed with Chrome 90.
The fix on the metric has been already committed in Chromium, you can find all the CLS metric change log here: https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/cls.md
Hope this helps.
@gilbertococchi thanks, just checked and indeed it is fixed Chrome 90.
Screenshot (Chrome 88 vs Chrome 90):
There is my fix for CLS for a full width swiper. Put this CSS before swiper.js initialized:
.swiper-container {
position: relative !important;
}
.swiper-slide-duplicate:first-child {
position: absolute !important;
width: 100% !important; /* seems like you can remove this line but I didn't test without it */
left: 0 !important;
}
.swiper-wrapper::before {
content: '' !important;
min-width: 100% !important;
}
Yep, I've been looking everywhere to solve my CLS issue. Changed the loop from true to false and the CLS went right down to 0!
Saying that, @m-haritonov CSS solution does exactly the same while the loop is on true. Nice one! 👍
The CSS solution didn't work for me. But disabling loop and autoplay initially, and enabling them after the page finished loading did:
$(window).load(function(e) {
if ( $('.swiper-container').length > 0 ) {
setTimeout(function(){
const swiper = document.querySelector('.swiper-container').swiper;
swiper.loop = true;
swiper.autoplay.start()
}, 3000);
}
});
Another potential CSS workaround for this issue: create a Aspect Ratio Box.
An Aspect Ratio Box can set a placeholder that takes up roughly the amount of space that the actual content will when loaded/initialized fully. This can reduce CLS by having consistent height taken up by pieces of content.
Get the aspect ratio of your largest image/item: Height / Width = ratio
And use that as a percentage padding for .swiper-container until it's initialized.
Note: .swiper-container[class='swiper-container']
will target when swiper is not initialized (the attribute selector is looking for the container with exactly that class value, so when initialized more classes get added and this CSS stops getting applied).
Example: Your image(s) is 1800px by 750px 750px / 1800px = 0.4166666667 % = 41.66666667
.swiper-container[class='swiper-container'] {
padding-top: 41.66666667%;
height: 0;
overflow: hidden;
position: relative;
}
.swiper-container[class='swiper-container'] .swiper-wrapper {
position: absolute;
top: 0;
left: 0;
}
It is fixed in Chrome 90. Confirmed.
Also it's worth mentioning that LCP scores are also affected. It gives me ~3-4 seconds on google speed page insight score, which is bad.
Also it's worth mentioning that LCP scores are also affected. It gives me ~3-4 seconds on google speed page insight score, which is bad.
Only because of Swiper? LCP is on your side. If you load 100 images in one slider, LCP can be 20 seconds, so how it is related to Swiper slider?
Also it's worth mentioning that LCP scores are also affected. It gives me ~3-4 seconds on google speed page insight score, which is bad.
Only because of Swiper? LCP is on your side. If you load 100 images in one slider, LCP can be 20 seconds, so how it is related to Swiper slider?
So far I was reading about that lately: https://itnext.io/javascript-sliders-will-kill-your-website-performance-5e4925570e2b
And it seems that there are probably faster libraries than SwiperJS (despite it's not described in this link), so there always is a room of improvement.
Also it's worth mentioning that LCP scores are also affected. It gives me ~3-4 seconds on google speed page insight score, which is bad.
Only because of Swiper? LCP is on your side. If you load 100 images in one slider, LCP can be 20 seconds, so how it is related to Swiper slider?
So far I was reading about that lately: https://itnext.io/javascript-sliders-will-kill-your-website-performance-5e4925570e2b
And it seems that there are probably faster libraries than SwiperJS (despite it's not described in this link), so there always is a room of improvement.
If you understand what you are doing, you can decrease the LCP as I wrote before - It mainly on your side not slider. The Swiper Demo has 100% in performance https://swiperjs.com/demos/370-lazy-load-images/core.html with less than 0.6s LCP score.
Ofcourse there are faster libraries than SwiperJS but when I see articles in 2020 with OwlCarousel or Slick as examples, it make me smile. Owl is dead for ~2 years, slick is still jQuery dependant.
Also it's worth mentioning that LCP scores are also affected. It gives me ~3-4 seconds on google speed page insight score, which is bad.
Only because of Swiper? LCP is on your side. If you load 100 images in one slider, LCP can be 20 seconds, so how it is related to Swiper slider?
So far I was reading about that lately: https://itnext.io/javascript-sliders-will-kill-your-website-performance-5e4925570e2b And it seems that there are probably faster libraries than SwiperJS (despite it's not described in this link), so there always is a room of improvement.
If you understand what you are doing, you can decrease the LCP as I wrote before - It mainly on your side not slider. The Swiper Demo has 100% in performance https://swiperjs.com/demos/370-lazy-load-images/core.html with less than 0.6s LCP score.
Ofcourse there are faster libraries than SwiperJS but when I see articles in 2020 with OwlCarousel or Slick as examples, it make me smile. Owl is dead for ~2 years, slick is still jQuery dependant.
Ok, so I pretty much tried most of the things for improving site performance - updating libraries, optimizing database queries using prefetches, using webp depending on page headers, rewriting code from jQuery to VanillaJS and getting rid of dependency (but now I know, that jQuery impact on performance is so small that nobody will notice it - the difference is defined by some float part of milisecond, so, not really any impact). I even use lazyloading at Swiper side. And all I achieved is 83-87 score performance on mobiles.
My website uses bootstrap 3 as template, and I don't know what I can do next. The performance is probably going down because of LCP score, which is 4.1s for really high compressed website. Can you post me an example how to get low LCP?
I have overwritten also init
method like this:
on: {
init: () => {
document.querySelectorAll('.swiper-pagination-bullets').forEach((el) => {
el.parentNode.removeChild(el);
});
}
}
For removing swiper pagination bullets.
Rest of my config looks like this:
new Swiper(sliderClass, {
preloadImages: false,
lazy: true,
autoplay: {
delay: 5000,
disableOnInteraction: false,
},
navigation: {
nextEl: '.home-slider-next',
prevEl: '.home-slider-prev',
},
loop: true,
on: {
init: () => {
document.querySelectorAll('.swiper-pagination-bullets').forEach((el) => {
el.parentNode.removeChild(el);
});
}
}
})
Thanks for help in advance.
^ : I gained 1-2 points of performance just using defer and async in correct manner, it seems that the initial page is stuck on loading and Swiper is executing after ~1-1.5 seconds.
Still for some reason LCP score is 4-4.5...
Can I also solve somehow this error by changing config:
Preload Largest Contentful Paint image
Hm?
Hello,
I avoided the CLS to 0 by applying just 1 rule to the parent container of the swiper
.gallery{aspect-ratio: 9 / 16;}
Hope it helps
/* Add this class to .swiper element */
.swiper-prevent-content-shift {
--swiper-sidebar-w: 0px;
--swiper-slidesPerView: 1;
--swiper-spaceBetween: 16px;
--swiper-available-width: 100vw - ( var(--page) * 2 ) - var(--swiper-sidebar-w);
--swiper-SwiperSlide-width: calc(
(
var(--swiper-available-width)
-
( ( var(--swiper-slidesPerView) - 1 ) * var(--swiper-spaceBetween) )
)
/
var(--swiper-slidesPerView)
);
@screen lg {
--swiper-sidebar-w: 14rem;
}
.swiper-slide {
width: var(--swiper-SwiperSlide-width) !important;
/* Only for horizontal swipers (only horizontal swipers need preventing content shift because vertical ones need fixed width and height) */
margin-left: var(--swiper-spaceBetween);
}
}
In my swiper component:
<Swiper class="ProductCardSwiper swiper-prevent-content-shift">
<!-- ... -->
</Swiper>
<style lang="postcss">
:global(.ProductCardSwiper.swiper-prevent-content-shift) {
@screen 5xs { --swiper-slidesPerView: 2 }
@screen xs { --swiper-slidesPerView: 3 }
@screen 2md { --swiper-slidesPerView: 4 }
@screen xl { --swiper-slidesPerView: 5 }
}
</style>
I used the code above (both code blocks) for my products, blog posts, gift cards swiper and etc. You can check it out here: https://farsgamer.vercel.app/ You can see the content shift here: https://farsgamer-1svyvc1ck-babakfp.vercel.app/
For the big main slider at the top of the home page, previously I was only using md:w-8/12
, now I added md:w-8/12 md:min-w-8/12 md:max-w-8/12
.
Look at the blog posts and gift cards to see the content shift happening. Ignore product boxes. In dev tools, you can put the network speed on 3G so you can easily see the content shift. You are going to see gift card boxes getting shifted from 1 per view to a few items per view.
Update:
I also add a few CSS aspect-ratio
styles to prevent content shifts even more. In dev tools > elements tab, search for a class called aspect-
.
Gtmetrix:
Lighthouse Desktop:
Lighthouse Mobile:
/*
---
Fixing Swiper content shift
---
*/
.swiper-prevent-content-shift {
--swiper-slidesPerView: 1;
--swiper-spaceBetween: 16px;
--swiper-SwiperSlide-width: calc(
(100%
- ( var(--swiper-slidesPerView) - 1 )
* var(--swiper-spaceBetween))
/ var(--swiper-slidesPerView)
);
.swiper-slide {
width: var(--swiper-SwiperSlide-width);
/* Only horizontal swipers need this because vertical ones have fixed width and height */
margin-left: var(--swiper-spaceBetween);
}
}
I have an element within my slide that is positioned to be at the bottom
{
position: fixed;
bottom: 0;
}
If I use any top
property then it will have CLS at 0, but using bottom it breaks it. Would love any feedback if anyone has any suggestions. I tried babakfp which unfortunately didn't work. Have also tried different aspect ratios but they tend to make it worse. Removing loop
helps, but still has a score above 0.2
I have an element within my slide that is positioned to be at the bottom
{ position: fixed; bottom: 0; }
If I use any
top
property then it will have CLS at 0, but using bottom it breaks it. Would love any feedback if anyone has any suggestions. I tried babakfp which unfortunately didn't work. Have also tried different aspect ratios but they tend to make it worse. Removingloop
helps, but still has a score above 0.2
Found a solution to ours -- adding height: 100vh
to the swiper container
now, 16th May 2023, layout shift issue still exists, but narrow down to those prev and next navigation button.
Lighthouse reported:
div#app > div.swiper > div.swiper-button-prev > ::after
<::after>
div#app > div.swiper > div.swiper-button-next > ::after
<::after>
I had a big problem with CLS on a site where there are several swiper components in use. ( https://strippoker.app/ ) The solution was to add a class to the images displayed in the components that defines their size for specific resolutions. Everyone needs to adapt a similar patch of their website appearance.
.image_presetsize {
aspect-ratio: 640/427;
@media (min-width: 1920px) and (max-width: 2560px) {
width: 19.33vw;
padding: 0 2px;
}
@media (min-width: 1200px) and (max-width: 1919px) {
width: 23.94vw;
padding: 0 2px;
}
}
etc. for each resolution (number of photos in a row) CLS after this operation is 0.
I have number of slides per view set to 5 and seems slider at first renders only 1 slide which ends up being full width of slider then it changes to 5 slides. This causes CLS increase.
Switching off loop did nothing, seems I have to find a different slider.
I developed a solution for a banner with 3 sliders using SwiperJS that was showing a CLS of 0.664.
.slide:not(.swiper-slide-active) {
display: none;
}
// Starts Swiper JS
on: {
init: function () {
setTimeout(() => {
const slides = document.querySelectorAll('.swiper-slide');
slides.forEach(function (slide) {
slide.style.display = 'block';
});
this.update(); // 'this' refers to the Swiper instance
}, 10);
}
}
With this solution, I achieved a CLS of 0.001 and it went back to performing at > 90/100 performance :)
@thiagodomene Your solution has indeed provided a slight enhancement to the performance of the sliders using SwiperJS on my website. However, the abundance of images within the slider is still leading to performance issues on the page.
@modbender I'm having the same issue, did you find any solution?
This fixed it for me:
.swiper-main .swiper-slide:not(:first-child) {
opacity:0;
width:1px;
}
This is a (multiple allowed):
[ ] bug
[x] enhancement
[ ] feature-discussion (RFC)
Swiper Version: 6.1.2
Platform/Target and Browser Versions: desktop Chrome
Live Link or JSFiddle/Codepen or website with isssue: https://wannacat.org/
What you did
Swiper is working perfectly, thank you very much for working on this project. I am trying to increase PageSpeed performance index, and the Lighthouse testing tool in Chrome reports large CLS (Cumulative Layout Shift) due to Swiper.
Expected Behavior
It would be great to have 0 CLS due to Swiper
Actual Behavior
Reported CLS is 0.874: https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fwannacat.org%2F&tab=desktop I have tested it and it is unfortunately due to Swiper. During the initialization, the Swiper container first disappears and then reappears, causing large layout shift. Is there any way to avoid this? Thank you.