glidejs / glide

A dependency-free JavaScript ES6 slider and carousel. It’s lightweight, flexible and fast. Designed to slide. No less, no more
https://glidejs.com
MIT License
7.28k stars 770 forks source link

only initialise the carousel if there are enough items to (at least) fill the available space #208

Open bw1984 opened 6 years ago

bw1984 commented 6 years ago

we have an implementation where our carousels will always have an indeterminate amount of items in and i'd like to be able to have the carousel intelligently initialise/destroy itself depending on whether the amount of items fit within the viewport. Currently if i have a carousel with 1 item in it gets repeated 5 times. Its behaving exactly as i'm telling it to but i just wondered if theres a way to add in some conditional logic so it will automatically decide whether or not to initialise

jedrzejchalubek commented 6 years ago

It's really hard to tell what you want to archive without some demo. You can simply call mount() method inside a length check.

if (items.length > 5) {
  glide.mount();
}
bw1984 commented 6 years ago

your suggestion is sensible but i am struggling to figure out how to reconcile it with responsiveness/breakpoints. I think basically what i'm after is a way to mount and unmount the carousel as part of the breakpoints functionality. To try to give a real world scenario...

The problem i have is that at sizes above 1200px my 3 items are being repeated, which i dont want, but if i only run mount() conditionally on initial page load (as you suggest) then i miss the opportunity to initialise the breakpoints, which i do want. The only other way i can think of is to implement my own breakpoint observer and run mount/unmount methods manually at varying screen sizes, which would bypass your breakpoints implementation entirely so seems a tad defeatist.

I think being able to mount and unmount at different breakpoints would be nice, although im not sure how that would work within the current implementation. Ultimately the goal is to not have elements (visibly) repeated/duplicated.

I hope this makes sense

brentkelly commented 5 years ago

In case its of any use to anyone I nearly got the following code working. It:

Unfortunately the destroy API method appears to leak & has memory of previous slides. So when I attempt to refresh the slide deck, it remembers the original slides regardless of the changed state.

I noted there used to be a refresh method - but I can't find one. Surely someone has tried to dynamically changes the slides in their deck before? Is there no way to do this?

Code below:

Glide.prototype._mount = Glide.prototype.mount;
Glide.prototype.mount = function() {

    // if already mounted, destroy
    if (this.mounted) {
        this.destroy();
    }

    // assumes a unique selector
    var element = document.querySelector(this.selector);
    var perView = this.settings.perView;
    var slideParent = element.querySelector('.glide__slides');

    // if there is no need to scroll, hide the controls & disable the slider
    var slideCount = element.querySelectorAll('li.glide__slide').length;
    if (slideCount <= perView) {
        element.classList.add('glide--disabled');
        this.disable();
    } else {
        element.classList.remove('glide--disabled');
        this.enable();
    }

    // add in any missing dummy slides
    for (; slideCount < perView; slideCount++) {
        var dummy = document.createElement('li');
        dummy.className = 'glide__slide glide__slide--dummy';
        slideParent.appendChild(dummy);
    }

    // remount
    this._mount(arguments);
    this.mounted = true;

    // store the current perView setting so we know when it's changed & we need to remount
    this.currentPerView = this.settings.perView;
}

// on destroy remove any dummy slides
Glide.prototype._destroy = Glide.prototype.destroy;
Glide.prototype.destroy = function() {
    this._destroy();
    document.querySelectorAll(this.selector + ' li.glide__slide--dummy').
        forEach(function(dummy) {
            dummy.parentNode.removeChild(dummy);
        });
};

// detect when a breakpoint has been hit & remount
var glide = new Glide(selector, settings);
glide.on('resize', function() {
    if (glide.settings.perView != glide.currentPerView) {
        glide.mount();
    }
});
glide.mount();
matt-bailey commented 5 years ago

Did anyone solve this in the end, I have the same requirement? I have 3 slides. On desktop they should just display in a row, no need to be in a carousel. On mobile however they should 'collapse' into a carousel. I suppose I could basically duplicate my content in the HTML - one block for desktop and one for mobile, and hide/show each block at the relevant breakpoints - but that's obviously not ideal.

svenbluege commented 5 years ago

My backend code renders a plain list of elements. JavaScript will pick them up and transform them into a slider. This allows me to show a different number of images per slide depending on the screen size. Don't forget that mobile devices have a landscape and a portrait mode.

My code is simple:

  1. check how many images I can fit into one slide
  2. create the slide markup for n slides
  3. fire up GlideJS on this markup

Posting code is a little bit difficult since it is integrated into my application.

matt-bailey commented 5 years ago

Thanks for the insight @svenbluege. So the way you've approached it is similar to @brentkelly? You're mounting and destroying Glide 'on-the-fly' based on how the slides fit into the current viewport? If there are too many you mount Glide, and if there aren't enough you destroy Glide, is that correct? And presumably you're doing this on window resize as well - constantly checking to see how the slides fit?

svenbluege commented 5 years ago

@matt-bailey I do not modify GlideJS. I just use it inside my "Slide Transformator". But other than that, you're correct.

dddeeemmmooonnn commented 4 years ago

my solution

let gallery = document.getElementsByClassName('image-gallery');
if (gallery.length !== 0) {
    let check_resize = (glide) => {
        if (glide.slides_count <= glide.settings.perView) {
            glide.update({startAt: 0}).disable();
        } else {
            glide.enable();
        }
    };
    [...gallery].forEach(el => {
        let el_glide = new Glide(el, {
            gap: 10,
            perView: 4,
            bound: true,
            breakpoints: {
                575: {
                    perView: 1,
                },
                767: {
                    perView: 3,
                },
            }
        });
        el_glide.slides_count = el.querySelectorAll('.glide__slide').length;
        el_glide.on('resize', () => {
            check_resize(el_glide);
        });
        el_glide.mount();
        check_resize(el_glide);
    });
}
khinkelthein-dotsource commented 4 years ago

my solution

let gallery = document.getElementsByClassName('image-gallery');
if (gallery.length !== 0) {
    let check_resize = (glide) => {
        if (glide.slides_count <= glide.settings.perView) {
            glide.update({startAt: 0}).disable();
        } else {
            glide.enable();
        }
    };
    [...gallery].forEach(el => {
        let el_glide = new Glide(el, {
            gap: 10,
            perView: 4,
            bound: true,
            breakpoints: {
                575: {
                    perView: 1,
                },
                767: {
                    perView: 3,
                },
            }
        });
        el_glide.slides_count = el.querySelectorAll('.glide__slide').length;
        el_glide.on('resize', () => {
            check_resize(el_glide);
        });
        el_glide.mount();
        check_resize(el_glide);
    });
}

Although I would prefer automatic destroy/remount by the plugin itself, your answer helped me for now. Thank you!

aaronmiller-ehouse commented 1 year ago

This is really high sensitive requirement.. Any updates for this?

hseager commented 8 months ago

It appears the duplication of items only happens when the Glide type is 'carousel'.

A solution that works in my case is changing the Glide back to a slider if there aren't enough items before the mount:

if (items && items.length < 4) {
   carousel.settings.type = "slider";
}

carousel.mount();

I haven't experimented with this but you can probably do the same in the breakpoint settings:

breakpoints: {
  768: {
    perView: 3,
    type: 'carousel'
  },
  992: {
    perView: 5,
    type: 'slider'
  },
}

Although this isn't ideal as it changes the way Glide behaves, it might be a workaround while a proper fix is considered.