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

horizontal splide auto height implementation for mobile #227

Closed
BloodyNails opened this issue Jan 15, 2021 · 17 comments
Closed

horizontal splide auto height implementation for mobile #227

BloodyNails opened this issue Jan 15, 2021 · 17 comments

Comments

@BloodyNails
Copy link

BloodyNails commented Jan 15, 2021

I'm having a horizontal splide show on my site. The problem I have is the splide show height. The tallest splide will determine the height of the entire splide show which causes the smaller ones to have massive gaps of whitespace. On mobile there is a great difference between the splides because of text which has to be displayed more vertically than horizontally which results in bigger splides if they contain lots of text.

If you don't know what I'm talking about here's a visualisation. I'm talking about a fullscreen splide so these grey bars are not seen at the same tame. Only when you swipe on your phone.

splide

I would love to have something like this. So the height is adaptive rather than static.
https://owlcarousel2.github.io/OwlCarousel2/demos/autoheight.html

@ghost
Copy link

ghost commented Jan 18, 2021

Would this not cause CLS?

If a user is trying to click a button further down and the slider changes (for whatever reason) their click could potentially also change.

@BloodyNails
Copy link
Author

@brandon-promoonly I didn't think about that. You're right the CLS value would probably be pretty high. I guess I have to reduce the height of the highest slide. Thank you

Maybe someone has another idea so I'll keep this issue open

@couplebuttons
Copy link

You could try something like this. You should manipulate the .splide__list height depending on the .splide__slide height:

<div class="splide">
	<div class="splide__track">
		<ul class="splide__list">
			<li class="splide__slide">Slide 01</li>
			<li class="splide__slide">Slide 02</li>
			<li class="splide__slide">Slide 03</li>
		</ul>
	</div>
</div>
<script>
/***/
        splide.on('active', function (e) {
            e.slide.parentElement.style.height = e.slide.getHeight() + 'px'
        })
/***/
</script>

@ayoubelmoudni
Copy link

Hello all,

I made a workaround with JS to achieve the desired behaviour

splide.on('move', function (newIndex, oldIndex, destIndex) {
    newIndex += 1;
    var _height = jQuery('#splide01-slide0' + newIndex + ' > div').outerHeight();
    jQuery('.splide__list').height(_height);
});

This will handle all slides except the first one, for that one, i waited for the page to load and change the height of .splide__list using the first id like so

jQuery('.splide__list').height(jQuery('#splide01-slide01 > div').outerHeight());

@NaotoshiFujita
Copy link
Collaborator

Close the issue due to the major version update.
Feel free to create a new issue or open a new discussion.


Splide will not support this kind of feature (just because I don't like it).

@rinart73
Copy link

rinart73 commented Dec 19, 2021

I decided to try Splide because of it's small size compared to Swiper and it's sad that dev doesn't feel like implementing this basic feature.
In any case it seems that @couplebuttons code does the job, but I suggest the following improvement. It will make sure that auto--height is applied immediately after slider is initialized and also on window resize:

let updateHeight = () => {
  let slide = slider.Components.Slides.getAt( slider.index ).slide;
  slide.parentElement.style.height = slide.offsetHeight + 'px';
};
let slider = new Splide( elem, {
  type: 'loop', //or slide
} )
.mount()
.on( 'active resized', updateHeight );
updateHeight();

Obviously you would need transition for .splide__list:

.splide__list {
  transition: height .2s;
}

And if you want to change height during sliding and not after the slide was changed then you need to resize .splide_track instead:

let updateHeight = newIndex => {
  let slide = slider.Components.Slides.getAt( typeof( newIndex ) == 'number' ? newIndex : slider.index ).slide;
  slide.parentElement.parentElement.style.height = slide.offsetHeight + 'px';
};
let slider = new Splide( elem, {
  type: 'loop', //or slide
} )
.mount()
.on( 'move resize', updateHeight );
updateHeight();
.splide__track {
  transition: height .2s;
}

@nickfmc
Copy link

nickfmc commented Mar 27, 2022

Thanks for taking the time @rinart73 kudo's! this may be since an update however but I had to add the following CSS and only the track version will transition.

.splide__list {
    align-items: flex-start;
}

@lszabolcs
Copy link

Another possible solution for this issues. This one is not requires additional JS, and also doesn't add explicit height values to the slider. In my case I have dynamic content on the slides (eg. load more), so the height of the slides can change.

.splide__list {
  align-items: flex-start;
}

.splide__slide:not(.is-active) {
  height: 0;    // or 100vh, or something what fits your case
}

@DianyelaMaldonado
Copy link

DianyelaMaldonado commented Apr 12, 2023

Just adding !important and adding specificity, inside a media query worked for me:

.splide {
  .splide__track {
      .splide__list {
          align-items: flex-start !important;
      }
  }
}
.splide__slide:not(.is-active) {
  height: 0 !important;    
}

@ankursehdev
Copy link

The CSS solution is great but the transition doesn't work. is there a workaround to make that work? I added `transition: height .3s' to the splide__slide and splide__track but didn't work

DianyelaMaldonado

@makhnanov
Copy link

makhnanov commented Jun 13, 2023

Hello @NaotoshiFujita and Splide users. Probably I can fix it.

I think that autoHeight need for it, but not.

Need just add:

    .splide__list {
        transition: all 300ms ease;
    }
    document.addEventListener("DOMContentLoaded", function () {
        var slider = new window.Splide('.splide', { arrows: false, pagination: false, type: "loop"});
        var heightMap = {};
        document.querySelectorAll(".splide__slide").forEach(function (e) {
            e.style.maxHeight = 0;
        });
        slider.on("mounted", function () {
            var i = 0;
            document.querySelectorAll(".splide__slide").forEach(function (e) {
                if (!e.classList.contains('splide__slide--clone')) {
                    heightMap[i] = e.scrollHeight;
                    i++
                }
            });
        });
        slider.on("active", function (e) {
            // or e.index for non loop sliders
            var maxHeight = heightMap[e.slideIndex] + "px";
            e.slide.style.maxHeight = maxHeight;
            e.slide.parentElement.style.maxHeight = maxHeight;
        });
        slider.mount();
    });

You can try demo here.

@kristiansp
Copy link

kristiansp commented Jun 18, 2023

I had a go at it now, making it into an extension. I'm not the greatest programmer in the world, so if anyone can improve on it, I'd be very happy for it – but it seems to work.

With this extension in your code, you can add it to the mount() function. I've added the following (default) settings, that can simply be added to the options object (or be omitted):

adjustableHeight: {
  whileSliding: true,
  speed: '0.4s',
},

You can keep the extension in the same file as where you're initializing the Splide slider, and add it to the splide like this:

mySplide.mount({AdjustableHeight});

Here's the extension:

export function AdjustableHeight( Splide, Components, options) {

  // Useful elements
  const track = Components.Elements.track;
  const list = Components.Elements.list;

  // Custom options or using defaults
  const defaults = {
    'whileSliding': true,
    'speed': '0.4s',
  }

  let settings = defaults;
  const custom = options.adjustableHeight;

  if (custom) {
    settings.whileSliding = custom.whileSliding ?? defaults.whileSliding;
    settings.speed = custom.speed ?? defaults.speed;
  }

  function mount() {
    const eventType = settings.whileSliding ? 'move active resize' : 'active resized';
    Splide.on( eventType, adjustHeight );
  }

  function adjustHeight() {

    // When "whileSliding" is true it means altering the track element, when false means altering the list
    let element = settings.whileSliding ? track : list;
    let slideHeight = Components.Slides.getAt( typeof( newIndex ) == 'number' ? newIndex : Splide.index ).slide.offsetHeight;

    // If changing track height, add additional padding on the track element to the total height
    let trackStyle = track.currentStyle || window.getComputedStyle(track);
    let trackPadding = parseInt(trackStyle.paddingTop) + parseInt(trackStyle.paddingBottom);
    let totalHeight = (settings.whileSliding) ? slideHeight + trackPadding : slideHeight;

    // Let flex items have individual heights
    list.style.alignItems = 'flex-start';

    // Set transition and height
    element.style.transition = 'height ' + settings.speed;
    element.style.height = totalHeight + 'px';
  }

  return {
    mount,
  };
}

EDIT: It only seems to work when using the prev/next navigation, not when dragging on mobile. Will have a further look tomorrow. It obviously has to do with events – the "dragging" event is of course not guaranteed to complete a transition, whereas clicking navigation is.

EDIT2: After some tweaks, it now also works with dragging, with the only caveat being that when dragging the carousel, the height will adjust after the slide has changed, even when whileDragging is set to true. I don't think there's any way around this without doing something like pre-empting which slide you are dragging towards, and resetting it if you don't complete the drag. This seems like overkill.

Also fixed a small bug that would cause a slight layout shift when whileDragging is set to false (I was falsely adding the padding from the track element to the list in that case).

@ankursehdev
Copy link

@kristiansp Thanks for this! It works great for me.
If you need to add this to React. You can create this function and use this like

1. Create a new file and export function and Import in the Splide file
import { AdjustableHeight } from './slider-extension';
----
2. Use in Splide
<Splide
  options={{
    type: 'loop',
    lazyLoad: 'sequential',
  }}
  extensions={{ AdjustableHeight }}
>

I had to add lazyLoad because I have images in the slider and the height was not getting re-calculated. You might not need it.

@pablo-sg-pacheco
Copy link

Hello @NaotoshiFujita and Splide users. Probably I can fix it.

I think that autoHeight need for it, but not.

Need just add:

    .splide__list {
        transition: all 300ms ease;
    }
    document.addEventListener("DOMContentLoaded", function () {
        var slider = new window.Splide('.splide', { arrows: false, pagination: false, type: "loop"});
        var heightMap = {};
        document.querySelectorAll(".splide__slide").forEach(function (e) {
            e.style.maxHeight = 0;
        });
        slider.on("mounted", function () {
            var i = 0;
            document.querySelectorAll(".splide__slide").forEach(function (e) {
                if (!e.classList.contains('splide__slide--clone')) {
                    heightMap[i] = e.scrollHeight;
                    i++
                }
            });
        });
        slider.on("active", function (e) {
            // or e.index for non loop sliders
            var maxHeight = heightMap[e.slideIndex] + "px";
            e.slide.style.maxHeight = maxHeight;
            e.slide.parentElement.style.maxHeight = maxHeight;
        });
        slider.mount();
    });

You can try demo here.

Thanks!

I had to change it from:
var maxHeight = heightMap[e.slideIndex] + "px";

To:
var maxHeight = heightMap[e.index] + "px";

@maxkopych
Copy link

@kristiansp thanks for extension works great. But what is newIndex , it's undefined?

@kristiansp
Copy link

kristiansp commented Sep 7, 2023

@maxkopych I can’t fully remember, I think this is something that I reused from someone’s earlier stab at this. I wasn’t sure if newIndex was used if jumping to a specific slide via navigation dots or not, so I left it in just in case. Could well be that it’s not needed, but would have to dig into it to be sure.

UPDATE: It was a holdover from rinart73’s suggestion. It should be safe to remove newIndex.

@carasmo
Copy link

carasmo commented Jan 27, 2024

@kristiansp -- Thank you so much!

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

No branches or pull requests