Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vertical Swiper + autoHeight #4599

Open
niklasp opened this issue May 22, 2021 · 43 comments
Open

Vertical Swiper + autoHeight #4599

niklasp opened this issue May 22, 2021 · 43 comments

Comments

@niklasp
Copy link

niklasp commented May 22, 2021

Is your feature request related to a problem? Please describe.
For many use cases, vertical sliders are the best solution. It would be a great addition to swiper if autoHeight would work for swipers with direction: 'vertical' as well.

Describe the solution you'd like
I would love to see a swiper that works just like the autoHeight mode in horizontal, it should measure the height of the current slide (or if more than one, the heighest) and adjust the wrapper height to it.

Describe alternatives you've considered
Fixed height sliders work well with vertical mode but that is not always the best solution.

Additional context
@nolimits4web mentioned that it is only for 'horizontal', is there a reason for that?

I tried with custom css js and events to calculate the height correctly in the correct place but could not find the right event with the right code needed. The closest I could get is this codesandbox:

https://codesandbox.io/s/swiper-vertical-forked-2gklb?file=/index.html

The wrapper gets the correct height but the slide offset is wrong. A hint for a workaround would also be helpful. I nonetheless consider autoHeight + direction: vertical a very good match.

@benjamin3105
Copy link

It seems that this problem still exists. I recon more people want to use the autoHeight + direction: vertical combination. Have you found a workaround for this @niklasp? I'm still looking but when I find a solution I will let you know if you still need it ;)

@hasenov
Copy link

hasenov commented Nov 26, 2021

+1, same problem

@tunztunztunz
Copy link

Also having this issue. It takes up infinite space and causes the entire thing to be a real pain to style.

@mohammedrezq
Copy link

+1, as it mentioned the height get messed up, hope there is a way to solve this issue on vertical direction (At the moment I have disabled the option on vertical direction to avoid this issue).

@andreomcn
Copy link

+1

Im trying to create a vertical thumbnail navigation for a ecommerce product gallery and this issue came up.

my temporaly solution was manually set swiper-slide class css height to auto. but this gets messed up when loop = true

@antoniofalcone89
Copy link

+1 same here.

Any update about this?

@prate3k
Copy link

prate3k commented Aug 4, 2022

+1 facing same issue.

Swiper vertical height does not work when slides are of different heights (with or without virtualization enabled). Setting height to auto breaks the logic of setting top position since it still gets set on the basis of <total height> / number of vertical slide. Is there any workaround for this.

@yuuk
Copy link

yuuk commented Aug 5, 2022

Any update about this?

@marcruecker
Copy link

+1

Any update on this would be great.

@kappi94
Copy link

kappi94 commented Sep 14, 2022

+1 would appreciate this feature

@kiyoshi-satoo
Copy link

This approach helped me.

https://codepen.io/pylvin/pen/WNJWexR

But it will not work if you use loop:true

@mostafa-nematpour
Copy link

+1, same problem

@mapsmarketing
Copy link

Same issue here. It would be great if autoHeight worked correctly with vertical

@enzocomics
Copy link

I would also love a fix for this to be implemented, please.

@marinvalentin
Copy link

marinvalentin commented Dec 20, 2022

Same problem

@barzles
Copy link

barzles commented Jan 18, 2023

same problem

1 similar comment
@cuonghuynh
Copy link

same problem

@mapsmarketing
Copy link

I've swapped it to use Slick Slider instead as the vertical nav works much better on it. It might not be as feature rich as swiper but at least it gets it done.

@sarah-baldin
Copy link

+1 same here :/ need it in combination with direction: "vertical" and loop: true. Any suggestions?

@shoxton
Copy link

shoxton commented Feb 14, 2023

+1 same here :/ need it in combination with direction: "vertical" and loop: true. Any suggestions?

I have ecommerce product page with the main (selected) image alongside with the product gallery on the left side.
I've managed to accomplish what I needed by doing the following steps:

  • a flex container with position: relative wrapping both sliders (vertical gallery slider and selected image slider).
  • flex: 1 on gallery slider (you can change this to fit the horizontal width you need)
  • flex: 5 on main image slider (you can change this to fit the horizontal width you need)
  • position: absolute on the .swiper-wrapper child from the gallery slider (prevents from growing endlessly)
  • slide images (img) from both sliders with width: 100%; height: 100%; object-fit: contain;

@realdaly
Copy link

realdaly commented Mar 1, 2023

same issue, any updates?

@mattbarnicle
Copy link

same here. switched to horizontal because i couldn't come up with a good solution.

@skyFi
Copy link

skyFi commented Mar 24, 2023

+1
Any update on this would be great.

@mrgvd
Copy link

mrgvd commented Mar 26, 2023

Just use this

.your-slider .swiper-slide{
height: auto!important;
}

@skyFi
Copy link

skyFi commented Mar 27, 2023

Thank you anyway, although it didn't work for me.

Just use this

.your-slider .swiper-slide{ height: auto!important; }

@JoshDHBW
Copy link

I have the same problem, a fix would be awesome.

@marinvalentin
Copy link

@ALL - just update to last version :)

@strarsis
Copy link

strarsis commented Apr 17, 2023

@marinvalentin: I am using the latest version of swiper (9.2.2) and still have this issue.

@mrgvd: In my particular case your fix (setting height: auto !important on the swiper slide element) indeed helped, thanks!

@Giovancruz
Copy link

Same here.

It looks like it's taking the height of the parent element to define the item height, if you set a fixed height for the swiper-wrapper then the slider item will work. It would be great to have an option like heightTarget or something like that which by default would be the parent element, but you could pass the children parameter, just guessing...

@hheinsoee
Copy link

hheinsoee commented Jul 4, 2023

because of #child_element

<div class="swiper-slide" >
 <div id="child_element">Slide 1< /div>
</div>

@ivanturnovski
Copy link

ivanturnovski commented Aug 4, 2023

Combination of both fixed the issue for me

autoHeight: true,

.swiper-slide {
height: auto !important;
}

@adantart
Copy link

Some people here is writing that the solution of ".swiper-slide { height: auto !important }" is working ... but I think it's not.

I took the original demo of autoHeight of Swiper and add that CSS ... as you can see when you scroll , there is an important glitch that you see the "height" of the slide but with the content of other slide:

https://codepen.io/cptacco/pen/XWorJYO

Any ideas, please ?

@mostafa-nematpour
Copy link

@adantart

probably not a good idea to give the height statically
but I hope it helps

https://codepen.io/mostafa-nematpour/pen/yLGBNOw

...

.swiper {
...
height: 100%;
}

.swiper-slide {
...
height: 100%;
}

@adantart
Copy link

@adantart

probably not a good idea to give the height statically but I hope it helps

https://codepen.io/mostafa-nematpour/pen/yLGBNOw

...

.swiper { ... height: 100%; }

.swiper-slide { ... height: 100%; }

THANK YOU VERY MUCH ... but I'm looking for a "auto height", so if a slide has less height, the container should be auto to that active slide height.

@adantart
Copy link

adantart commented Aug 15, 2023

Ok friends, for those who do not work with the proposed CSS solution, I propose another new solution that has worked for me.

The problem is that the snapGrid is miscalculated, and I think I could fix it in a very dirty way, but as the code is not mine, and I didn't want to analyze the 9000 lines of code of the deminified code, I preferred to opt for a solution that may be a little "dirty" but works like a charm.

It is to use the events so, when Swiper finishes making its calculations and move the translate3D of the wrapper to the position that he believes correct (which is what fails, as the author of this thread said), move it manually calculating the height that should be moved, which is the sum of the heights of the previous divs with the sum of the space between them.

Here is the code:

swiper.on('transitionEnd', function(s) {
   var heightsSumUpToCurrentIndex = s.slides.slice(0, s.realIndex).reduce((sum, div) => sum + div.clientHeight, 0)
   newTransformWellCalculated = - heightsSumUpToCurrentIndex - (s.params.spaceBetween * s.realIndex);
   const match = /translate3d\(([^,]+),\s*([^,]+),\s*([^)]+)\)/.exec(s.wrapperEl.style.transform);
   if (match) {
      s.wrapperEl.style.transform = `translate3d(${match[1]}, ${newTransformWellCalculated}px, ${match[3]})`;
   }
});

You can find the CodePen updated (from my original post 1 hour ago) here:
https://codepen.io/cptacco/pen/OJrLyLd

@cbarretta12
Copy link

cbarretta12 commented Aug 16, 2023

Setting a max-height on the swiper-wrapper worked well for me. You may need to play around with it for your use case depending on your slides per view. Didn't need to use the swiper autoHeight or max-height auto on the slides. Haven't tested with looping or anything, just the standard vertical swiper.

@VirtualAkna
Copy link

I had the same problem, the solution I found was to rewrite the functions that calculate the height of the wrapper and the one that updates the slides.

It's not perfect and it could be much better with a bit of work, but it works in my case.

CSS :

.swiper {
    width: 100%;
}
.swiper-autoheight, .swiper-autoheight .swiper-slide {
    height: auto;
}

HTML :

<div class="swiper mySwiper">
    <div class="swiper-wrapper">
        <div class="swiper-slide">Slide 1</div>
        <div class="swiper-slide">Slide 2</div>
        <div class="swiper-slide">Slide 3</div>
        <div class="swiper-slide">Slide 4</div>
        <div class="swiper-slide">Slide 5</div>
        <div class="swiper-slide">Slide 6</div>
        <div class="swiper-slide">Slide 7</div>
        <div class="swiper-slide">Slide 8</div>
        <div class="swiper-slide">Slide 9</div>
    </div>
</div>

JS :

let swiper = new Swiper(".mySwiper", {
    autoHeight: true,
    direction: "vertical",
    slidesPerView: 3,
    loop: true,
    on: {
        init: function (swiper) {
            swiper.updateSlides = function () {

                function getDirectionLabel(property) {
                    return {
                        'width': 'height',
                        'margin-top': 'margin-left',
                        'margin-bottom ': 'margin-right',
                        'margin-left': 'margin-top',
                        'margin-right': 'margin-bottom',
                        'padding-left': 'padding-top',
                        'padding-right': 'padding-bottom',
                        'marginRight': 'marginBottom',
                    }[property];
                }

                const params = swiper.params;
                const {wrapperEl, slidesEl, size: swiperSize, rtlTranslate: rtl, wrongRTL} = swiper;
                const isVirtual = swiper.virtual && params.virtual.enabled;
                const previousSlidesLength = isVirtual ? swiper.virtual.slides.length : swiper.slides.length;
                const slides = swiper.slides;
                const slidesLength = isVirtual ? swiper.virtual.slides.length : slides.length;
                let snapGrid = [];
                const slidesGrid = [];
                const slidesSizesGrid = [];
                let offsetBefore = params.slidesOffsetBefore;
                if (typeof offsetBefore === 'function') {
                    offsetBefore = params.slidesOffsetBefore.call(swiper);
                }
                let offsetAfter = params.slidesOffsetAfter;
                if (typeof offsetAfter === 'function') {
                    offsetAfter = params.slidesOffsetAfter.call(swiper);
                }
                const previousSnapGridLength = swiper.snapGrid.length;
                const previousSlidesGridLength = swiper.slidesGrid.length;
                let spaceBetween = params.spaceBetween;
                let slidePosition = -offsetBefore;
                let prevSlideSize = 0;
                let index = 0;
                if (typeof swiperSize === 'undefined') {
                    return;
                }
                if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) {
                    spaceBetween = (parseFloat(spaceBetween.replace('%', '')) / 100) * swiperSize;
                } else if (typeof spaceBetween === 'string') {
                    spaceBetween = parseFloat(spaceBetween);
                }
                swiper.virtualSize = -spaceBetween;
                // reset margins
                slides.forEach((slideEl) => {
                    if (rtl) {
                        slideEl.style.marginLeft = '';
                    } else {
                        slideEl.style.marginRight = '';
                    }
                    slideEl.style.marginBottom = '';
                    slideEl.style.marginTop = '';
                });
                // Calc slides
                let slideSize;
                for (let i = 0; i < slidesLength; i += 1) {
                    slideSize = 0;
                    let slide;
                    if (slides[i]) {
                        slide = slides[i];
                    }
                    slideSize = slide.offsetHeight;
                    if (slides[i]) {
                        slides[i].swiperSlideSize = slideSize;
                    }
                    slidesSizesGrid.push(slideSize);
                    if ((index - Math.min(swiper.params.slidesPerGroupSkip, index)) % swiper.params.slidesPerGroup === 0) {
                        snapGrid.push(slidePosition);
                    }
                    slidesGrid.push(slidePosition);
                    slidePosition = slidePosition + slideSize + spaceBetween;
                    swiper.virtualSize += slideSize + spaceBetween;
                    prevSlideSize = slideSize;
                    index += 1;
                }
                swiper.virtualSize = Math.max(swiper.virtualSize, swiperSize) + offsetAfter;
                if (rtl && wrongRTL && (params.effect === 'slide' || params.effect === 'coverflow')) {
                    wrapperEl.style.width = `${swiper.virtualSize + spaceBetween}px`;
                }
                if (params.setWrapperSize) {
                    wrapperEl.style[getDirectionLabel('width')] = `${swiper.virtualSize + spaceBetween}px`;
                }
                // Remove last grid elements depending on width
                if (!params.centeredSlides) {
                    const newSlidesGrid = [];
                    for (let i = 0; i < snapGrid.length; i += 1) {
                        let slidesGridItem = snapGrid[i];
                        if (params.roundLengths) slidesGridItem = Math.floor(slidesGridItem);
                        if (snapGrid[i] <= swiper.virtualSize - swiperSize) {
                            newSlidesGrid.push(slidesGridItem);
                        }
                    }
                    snapGrid = newSlidesGrid;
                    if (Math.floor(swiper.virtualSize - swiperSize) - Math.floor(snapGrid[snapGrid.length - 1]) > 1) {
                        snapGrid.push(swiper.virtualSize - swiperSize);
                    }
                }
                if (snapGrid.length === 0) snapGrid = [0];
                if (spaceBetween !== 0) {
                    const key = swiper.isHorizontal() && rtl ? 'marginLeft' : getDirectionLabel('marginRight');
                    slides.filter((_, slideIndex) => {
                        if (!params.cssMode || params.loop) return true;
                        if (slideIndex === slides.length - 1) {
                            return false;
                        }
                        return true;
                    }).forEach((slideEl) => {
                        slideEl.style[key] = `${spaceBetween}px`;
                    });
                }
                Object.assign(swiper, {
                    slides,
                    snapGrid,
                    slidesGrid,
                    slidesSizesGrid,
                });
                if (slidesLength !== previousSlidesLength) {
                    swiper.emit('slidesLengthChange');
                }
                if (snapGrid.length !== previousSnapGridLength) {
                    if (swiper.params.watchOverflow) swiper.checkOverflow();
                    swiper.emit('snapGridLengthChange');
                }
                if (slidesGrid.length !== previousSlidesGridLength) {
                    swiper.emit('slidesGridLengthChange');
                }
                if (params.watchSlidesProgress) {
                    swiper.updateSlidesOffset();
                }
            }

            swiper.updateAutoHeight = function (speed) {

                const activeSlides = [];
                const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
                let newHeight = 0;
                let i;
                if (typeof speed === 'number') {
                    swiper.setTransition(speed);
                } else if (speed === true) {
                    swiper.setTransition(swiper.params.speed);
                }
                const getSlideByIndex = index => {
                    if (isVirtual) {
                        return swiper.slides[swiper.getSlideIndexByData(index)];
                    }
                    return swiper.slides[index];
                };
                // Find slides currently in view
                if (swiper.params.slidesPerView !== 'auto' && swiper.params.slidesPerView > 1) {
                    if (swiper.params.centeredSlides) {
                        (swiper.visibleSlides || []).forEach(slide => {
                            activeSlides.push(slide);
                        });
                    } else {
                        for (i = 0; i < Math.ceil(swiper.params.slidesPerView); i += 1) {
                            const index = swiper.activeIndex + i;
                            if (index > swiper.slides.length && !isVirtual) break;
                            activeSlides.push(getSlideByIndex(index));
                        }
                    }
                } else {
                    activeSlides.push(getSlideByIndex(swiper.activeIndex));
                }

                // Find new height from highest slide in view
                if (swiper.params.direction === 'vertical') {
                    for (i = 0; i < activeSlides.length; i += 1) {
                        if (typeof activeSlides[i] !== 'undefined') {
                            const height = activeSlides[i].offsetHeight;
                            newHeight += height;
                        }
                    }
                } else {
                    for (i = 0; i < activeSlides.length; i += 1) {
                        if (typeof activeSlides[i] !== 'undefined') {
                            const height = activeSlides[i].offsetHeight;
                            newHeight = height > newHeight ? height : newHeight;
                        }
                    }
                }

                // Update Height
                if (newHeight || newHeight === 0) swiper.wrapperEl.style.height = `${newHeight}px`;
            }
        },
    },
});

@buidanmo
Copy link

This helped me:
direction: "vertical",
slidesPerView: "auto"
Try it to see if it help you!

@yakohere
Copy link

This helped me: direction: "vertical", slidesPerView: "auto" Try it to see if it help you!

this helped me, thanks

@unistudioco
Copy link

This "HACK" works perfectly even loop is set to true!

var swiper_thumbs = new Swiper(".swiper-thumbs", {
    items: 3,
    direction: 'vertical',
    autoHeight: true,
    freeMode: false,
    on:{
      init:function(){
        setSlideHeight(this);
      },
      slideChangeTransitionEnd:function(){
        setSlideHeight(this);
      }
    }
});
function setSlideHeight(el){
    $('.swiper-slide').css({height:'auto'});
    var currentSlide = el.activeIndex;
    var newHeight = $(el.slides[currentSlide]).height();

    $('.swiper-wrapper, .swiper-slide').css({ height : newHeight })
    el.update();
}

@yunjin-kim-giantstep
Copy link

yunjin-kim-giantstep commented Apr 1, 2024

This helped me: direction: "vertical", slidesPerView: "auto" Try it to see if it help you!

i explain more.
if you want act like this page footer, link

react

   <Swiper
        className={swiperStyle}
        direction="vertical"
        slidesPerView='auto'
        autoHeight={true}
        mousewheel={true}
        pagination={{
          clickable: true,
        }}
        modules={[Mousewheel, Pagination]}
        onSwiper={(swiper) => {
          swiper.mousewheel.enable();
          setSwiper(swiper);
        }}
        speed={1000}
      >
        <SwiperSlide>
          <div className={wrapStyle}>0</div>
        </SwiperSlide>

        <SwiperSlide>
          <div className={wrapStyle}>1</div>
        </SwiperSlide>

        <SwiperSlide>
          <Footer />
        </SwiperSlide>
      </Swiper>
    </>
    

css

export const swiperStyle = style({
  width: '100%',
  height: ['100vh', '100dvh'],

  overflow: 'hidden',
});

export const footerSwiperStyle = style({
  width: '100%',
  height: '60px',
})

export const wrapStyle = style({
  width: '100%',
  height: ['100vh', '100dvh'],

  textAlign: 'center',
  backgroundColor: 'rgba(0, 0, 0, 0.05)',
});

@buidanmo thanks!!! 😊

@Leandro-Lucena
Copy link

I just change height parameter (100% to 100vh)

.swiper-slide{
height: 100vh ;
}

and works fine!

@onlyhasbi
Copy link

<SwiperSlide className="rounded-[18px] text-[22px] font-bold text-white text-center" key={item} > <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"> <p className="my-">Slide {item + 1}</p> </div> </SwiperSlide>

make absolute position on SwiperSlide children element. its work for `EffectCards`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests