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

Change settings at breakpoint #233

Open
coreyworrell opened this issue Sep 9, 2015 · 42 comments
Open

Change settings at breakpoint #233

coreyworrell opened this issue Sep 9, 2015 · 42 comments

Comments

@coreyworrell
Copy link

It would be nice to do something similar to slick where you can define settings that only kick in at certain breakpoints.

For example, on a wide screen I would like to show multiple items and allow free scrolling, but on a smaller mobile device I would like to only show one item at a time and snap to each item. I see that the showing one at a time option is doable now by setting the width to 100%, but changing freeScroll is not possible currently (as far as I can tell).

Is there a simple way to handle this?

Thank you.

@desandro
Copy link
Member

Thanks for this feature request. +1 this issue if you'd like to see this feature added.

I've tried to make Flickity as flexible as possible so that layouts are handled entirely with your CSS. There's also the watchCSS option that allows you to enable or disable Flickity with breakpoints.

I'm not keen on adding this feature as its added complexity with small benefit. It requires mixing CSS logic with JS logic, which can be problematic. Many options, like freeScroll, assume you're only setting them at initialization. Changing options on the fly would require a large overhaul. That said, I'm always interested to see what developers are really wishing for.

@richgcook
Copy link

http://flickity.metafizzy.co/options.html#watchcss is just what I was after... being able to destroy/init the slider with your CSS/media queries is a game changer. Nice work, @desandro.

@piotrkulpinski
Copy link

+1

2 similar comments
@amdad
Copy link

amdad commented Dec 24, 2015

+1

@nonlinearcom
Copy link

+1

@michaelwoodruff
Copy link

+1

The Owl Carousel has these options:
http://www.owlcarousel.owlgraphic.com/demos/responsive.html

@warrebuysse
Copy link

+1 indeed, I hoped to see the same like what Owl Carousel has. Before I've been using Owl Carousel in pretty much every project. Our team changed to Flickity because it was very promising. This feature however is key.

An alternative solution is to make two options. 1 would be mouseDraggable and the other would be touchDraggable. So we still have the touchdrag on mobile & tablets.

@addedlovely
Copy link

+1

1 similar comment
@linnett
Copy link

linnett commented Apr 28, 2016

+1

@ghost
Copy link

ghost commented May 10, 2016

+1

@desandro
Copy link
Member

You can set options according to media queries with matchMedia. This will set the option on initialization, but not on resize. Some options are hard to re-set after initialization, like freeScroll, draggable, and cellAlign. But I think this solution will suit most of your requests. See demo http://codepen.io/desandro/pen/xVBpqG

// Flickity options, defaults
var options = {
  prevNextButtons: false
};

// enable prev/next buttons at 768px
if ( matchMedia('screen and (min-width: 768px)').matches ) {
  options.prevNextButtons = true;
}

// disable draggable at 1200px
if ( matchMedia('screen and (min-width: 1200px)').matches ) {
  options.draggable = false;
}

$('.gallery').flickity( options );

@desandro
Copy link
Member

This demo is now linked in Extra demos

@portedison
Copy link

portedison commented Apr 20, 2017

Using vanilla js, I found this worked.

// force options to transition instantly
featureFlkty.options.selectedAttraction = 1;
featureFlkty.options.friction = 1;
// select item
featureFlkty.select(i);
// reset defaults
featureFlkty.options.selectedAttraction = 0.025;
featureFlkty.options.friction = 0.28;

@desandro
Copy link
Member

@portedison You can select instantly with the third parameter in select

flkty.select( index, isWrapped, isInstant )
//
flkty.select( i, true, true )

@nChauhan91
Copy link

Can these options be somehow used on resize, I'm trying to implement few options without re-creating the slider on resize, i'm able to pass correct value to the plugin but it doesn't work while resizing.

Thanks

@EarthlingDavey
Copy link

EarthlingDavey commented Oct 23, 2017

Heres a quick snippet of how I change the flickity options on resize. It's specific for my project but should be easy to adapt, sorry I don't have time to make a more generic example at the moment...

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
    var timeout;
    return function () {
        var context = this, args = arguments;
        var later = function () {
            timeout = null;
            if (!immediate) {
                func.apply(context, args);
            }
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) {
            func.apply(context, args);
        }
    };
}

var latestMachines = debounce(function () {

    var $latestMachines = $('.latest-machines .product-cards>.row');

    // set flickity options - small first
    var latestMachinesOptions = {
        // options
        cellAlign: 'left',
        contain: true,
        prevNextButtons: false,
        pageDots: false,
        wrapAround: true,
        groupCells: 1
    };

    // group cells and align center for large
    if (matchMedia('screen and (min-width: 1000px)').matches) {
        latestMachinesOptions.groupCells = 2;
        latestMachinesOptions.cellAlign = 'center';
    }

    if ($latestMachines.hasClass('flickity-enabled')) {

        // get flickity data
        var flkty = $latestMachines.data('flickity');

        if (latestMachinesOptions.groupCells == flkty.options.groupCells || latestMachinesOptions.cellAlign == flkty.options.cellAlign) {
            // do nothing, flickity options are correct
            return false;
        } else {
            // flickity options are incorrect
            // destroy slider
            $latestMachines.flickity('destroy');
        }
    }
    // init (or re-init) flickity
    $latestMachines.flickity(latestMachinesOptions);

}, 250);

if ($('.latest-machines .product-cards>.row').length > 0) {

    latestMachines();
    window.addEventListener('resize', latestMachines);

}

@jerome-toole
Copy link

The new draggable: '>1' option takes care of this if all you want to do is disable dragging. If you want to disable flickity based on slide count, then I found this solution to work well (using watchCSS):

#278 (comment)

@saltcod
Copy link

saltcod commented May 11, 2018

Hey all. I didn't see this example out there, so I wanted to leave it here.

I want to instantiate flickity for small screens, then destroy it when we get up to large screens. I'm using enquirejs for the media query testing, but matchMedia would work too. This might not be the right/best way to do this, but I think it does work.

import enquire from 'enquire-js';
import Flickity from 'flickity';

/**
 * Initialize flickity slider on mobile, destroy it when we go
 * back to desktop width
 */

const columnSlider = document.querySelector('.columns');

export default enquire.register('screen and ( max-width: 767px )', {
	match: () => {
		const flkty = new Flickity(columnSlider, {
			cellAlign: 'left',
			contain: true,
			pageDots: false
		});
	},
	unmatch: () => {
		const unflkty = new Flickity(columnSlider).destroy();
	}
});

@caseyjhol
Copy link

@desandro Why is this issue closed? As far as I can tell, it's still not possible to change options after initialization.

@tpinne
Copy link

tpinne commented Sep 24, 2018

The linked demo in this comment only shows how to set options based on breakpoint on page load but not on page resize.

@mikespineu
Copy link

Why this issue is closed? Demos provided by desandro doesn't resolve problems with different settings for breakpoints for example "groupCells".

@desandro desandro reopened this Oct 5, 2018
@saltcod
Copy link

saltcod commented Oct 5, 2018

Not directly solved with the plugin, but my solution works perfectly using enquire or just plain match media:

#233 (comment)

@Julix91
Copy link

Julix91 commented Apr 2, 2019

Another attempt at this - handles multiple carousels in case you have multiple in one page that share the same break point. Should probably add logic to handle being given a non-array (i.e. just one carousel) as well ...

function toggleDraggableAtFlickityBreakpoint(mediaQuery, $carousels) {

	if(!mediaQuery || !$carousels) { return; }

	var dragToggles = function (match) {
		$carousels.forEach(function($carousel) {
			var flkty = $carousel.data('flickity');
			if(!flkty) { return; }

			if (match) {
				flkty.options.draggable = true;
			} else {
				flkty.options.draggable = false;
			}
			flkty.updateDraggable();
		});
	};

	var mql = window.matchMedia(mediaQuery);
	dragToggles(mql.matches);

	// listener
	mql.addListener(function(e){
		dragToggles(e.matches);
	});
}
// examples
toggleDraggableAtFlickityBreakpoint("(max-width: 768px)", [$carouselMain, $thatSecondSlider]);
toggleDraggableAtFlickityBreakpoint("(max-width: 1024px)", [$thatMoreDraggableSlider]);

@codefinite
Copy link

codefinite commented Jun 6, 2019

I've recently migrated from Slick and have been sorely missing this feature, I've come up with an idea to leverage the existing watchCSS so it can do more than just enable/disable Flickity, by adding the ability to trigger setting changes. Here's an example of how it could work:

JS

var flkty = new Flickity( elem, {
  watchCSS: true
  groupCells: 2,
  responsive: {
    small: {
      groupCells: 1
    }
  }
});

CSS

/* enable Flickity default settings */
.carousel:after {
  content: 'flickity';
  display: none; /* hide :after */
}

@media screen and ( max-width: 480px ) {
  /* enable Flickity small settings */
  .carousel:after {
    content: 'flickity.small';
  }
}

You have a new setting, an object for storing your breakpoints, (in the example I've named it "responsive"), the property names become the identifiers for your breakpoint and you trigger them by appending the breakpoint identifier to the .carousel:after content. The breakpoints contain an object where you can override any of the existing Flickity settings.

This keeps the settings and styles separate, yet allows for triggering settings changes with any CSS media query.

Edit:

I've put together a proof of concept that seems to be working, I found that Flickity only checks that watchCSS is truthy, so no need to add a new option, we can put the responsive object as the value of watchCSS and watchCSS will still evaluate as true.

var flkty = new Flickity( elem, {
  watchCSS: {
    small: {
      groupCells: 1
    }
  },
  groupCells: 2
});

CSS as above

if (typeof Flickity === 'function') {

  var proto = Flickity.prototype;

  proto.watchCSS = function () {
    var watchOption = this.options.watchCSS;
    if (!watchOption) {
      return;
    }

    var afterContent = getComputedStyle(this.element, ':after').content;
    // activate if :after { content: 'flickity' }
    if (afterContent.indexOf('flickity') != -1) {
      // ---modification start---
      // check for watchCSS options changes
      if (typeof watchOption === 'object') {
        this.watchCSSOptionsChange(afterContent);
      }
      // ---modification end---
      this.activate();
    } else {
      this.deactivate();
    }
  };

  proto.watchCSSOptionsChange = function (afterContent) {
    // store the current breakpoint identifier
    if (!this._currentOptionsIdentifier) {
      this._currentOptionsIdentifier = '';
    }
    // trim flickity. and surrounding quotes from the new breakpoint identifier
    var identifier = afterContent.substring(0, afterContent.length - 1).substring(10);

    // check for breakpoint change
    if (this._currentOptionsIdentifier !== identifier) {

      console.log(identifier);

      // if the original options have been cloned apply them to reset
      if (typeof this.options._flickityInitialOptions === 'object') {

        this.options = this.options._flickityInitialOptions;
        this._currentOptionsIdentifier = '';

        console.log('reset');
      }

      // check if the new breakpoint options exist
      if (typeof this.options.watchCSS[identifier] === 'object') {

        // clone the original options so we can reset on breakpoint change
        this.options._flickityInitialOptions = JSON.parse(JSON.stringify(this.options));

        // apply the new options
        var newOptions = this.options.watchCSS[identifier];

        for (var key in newOptions) {
          if (newOptions.hasOwnProperty(key)) {
            this.options[key] = newOptions[key];
          }
        }

        // update the identifer so we can skip if there's no change in breakpoint
        this._currentOptionsIdentifier = identifier;

        console.log('responsive');
      }

      console.log(this.options);
    }
  }
}

I'm modifying the watchCSS method to check if options.watchCSS is an object, if not the only additional code running is a typeof check.

The new watchCSSOptionsChange function could potentially be stand alone and triggered using the ready and resize events to avoid modifying the watchCSS method.

@mrspence
Copy link

mrspence commented Oct 4, 2019

Any update on this?

@bobbaker404
Copy link

bobbaker404 commented Oct 10, 2019

For my purposes, I wanted to change the groupCells based on breakpoint. I'm sure this method can be used with changing other Flickity options as well.

My plan was to initi Flickity based on the current breakpoint (xs, sm, md, lg, xl, xxl). 1 groupCells for xs, sm & md; 2 groupCells for anything bigger. On resize, check the breakpoint & reset the number of groupCells. If the number of groupCells changed, destroy Flickity & re-init with the new groupCells count.

I found this lovely article about setting breakpoints in CSS & using them within Javascript, so I gave it a try and I'm quite happy with the results.

Here's my code:

    // Flickity set-up
    var flkty;
    var elem = document.querySelector('.testimonial-carousel');
    var groupCells;

    // Set-up the breakpoint variable
    var breakpoint;

    // Get the current breakpoint
    var getBreakpoint = function () {
      return window.getComputedStyle(document.body, ':before').content.replace(/"/g, '');
    };

    // Set the number of groupCells based on breakpoint
    var setCells = function () {
      if (breakpoint === 'xs' || breakpoint === 'sm' || breakpoint === 'md') {
        return 1;
      } else {
        return 2;
      }
    };

    // Calculate breakpoint on page load
    breakpoint = getBreakpoint();
    groupCells = setCells();
    // console.log(groupCells);
    do_flickity();

    // Setup a timer
    var timeout;

    // Recalculate breakpoint on resize
    window.addEventListener('resize', function () {
      if (timeout) {
        window.cancelAnimationFrame(timeout);
      }
      timeout = window.requestAnimationFrame(function () {

        // Get the groupCells value from before resize
        var prevGroupCells = groupCells;

        // Get the new current breakpoint
        breakpoint = getBreakpoint();

        // Get the new groupCells value
        groupCells = setCells();

        // If new groupCells count is different from previous. DESTROY Flickity & init new Flickity
        // console.log(groupCells + ' | ' + prevGroupCells);
        if( groupCells !== prevGroupCells ){
          flkty.destroy();
          do_flickity();
        }

      });
    }, false);

    // init Flickity
    function do_flickity() {
      flkty = new Flickity( elem, {
        groupCells: groupCells,
        prevNextButtons: false,
      });
    }

@Hlsgs
Copy link

Hlsgs commented May 24, 2020

@desandro
This is my and many others' deal braker when it comes to migrating to Flickity.

@zzseba78
Copy link

Any news on this?

@kazunorimiura
Copy link

+1

1 similar comment
@obliviga
Copy link

+1

@umkasanki
Copy link

+8908765 !!!

@nikkster311
Copy link

What about this? https://flickity.metafizzy.co/options.html
image

@TomSinclair
Copy link

Any update on bringing this functionality to Flickity?

@phucbm
Copy link

phucbm commented Aug 14, 2021

Hi guys,

I've been using Flickity for a while and have had an urge for this responsive option, so I created a jQuery plugin to handle this and am quite happy with the result, here is how I control responsive with the extra plugin

// Init Flickity
$carousel.flickityResponsive({
    cellAlign: "left",
    contain: true,
    freeScroll: true,
    responsive: [
        {
            breakpoint: 1024,
            settings: {
                wrapAround: true,
                cellAlign: "center",
                freeScroll: false,
                prevNextButtons: false,
                pageDots: false
            }
        }
    ]
});

The plugin will decide when to destroy and re-initialize the carousel to apply new options using matchMedia(). You might notice that the usage was inspired by Slick.
I published the code at my repository for maintenance purposes 👉 https://github.com/phucbm/flickity-responsive
There's also a CodePen for it 👉 https://codepen.io/phucbui/pen/ExmJVZa

@davemac
Copy link

davemac commented Aug 30, 2021

Thankyou @phucbm this is just what I was looking for. It seems to be a very common requirement that groupCells should change on different screen sizes - would be great to see this added to Flickity.

@obliviga
Copy link

+1

1 similar comment
@kevin-vo
Copy link

+1

@fozdemir40
Copy link

Works amazing @phucbm thank you!

@dijkermans
Copy link

+1

1 similar comment
@hiphopappotamus
Copy link

+1

@joebentaylor1995
Copy link

Such a shocking and disappointing decision to make this an extra - rather than integrated into the slider... glad i went to SwiperJS

@scheylon
Copy link

scheylon commented Apr 4, 2023

Still no breakpoint feature ?

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

No branches or pull requests