flickity icon indicating copy to clipboard operation
flickity copied to clipboard

Change settings at breakpoint

Open coreyworrell opened this issue 8 years ago • 42 comments

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.

coreyworrell avatar Sep 09 '15 22:09 coreyworrell

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.

desandro avatar Sep 11 '15 11:09 desandro

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.

richgcook avatar Sep 18 '15 09:09 richgcook

+1

piotrkulpinski avatar Dec 21 '15 12:12 piotrkulpinski

+1

amdad avatar Dec 24 '15 16:12 amdad

+1

nonlinearcom avatar Feb 19 '16 17:02 nonlinearcom

+1

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

michaelwoodruff avatar Feb 19 '16 18:02 michaelwoodruff

+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.

warrebuysse avatar Mar 08 '16 18:03 warrebuysse

+1

addedlovely avatar Mar 15 '16 20:03 addedlovely

+1

linnett avatar Apr 28 '16 08:04 linnett

+1

ghost avatar May 10 '16 15:05 ghost

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 avatar May 11 '16 13:05 desandro

This demo is now linked in Extra demos

desandro avatar May 25 '16 14:05 desandro

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;

portedison avatar Apr 20 '17 22:04 portedison

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

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

desandro avatar Apr 21 '17 12:04 desandro

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

nChauhan91 avatar Jul 13 '17 08:07 nChauhan91

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);

}

EarthlingDavey avatar Oct 23 '17 10:10 EarthlingDavey

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):

https://github.com/metafizzy/flickity/issues/278#issuecomment-266070668

jerome-toole avatar Apr 18 '18 17:04 jerome-toole

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();
	}
});

saltcod avatar May 11 '18 14:05 saltcod

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

caseyjhol avatar Aug 29 '18 22:08 caseyjhol

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

tpinne avatar Sep 24 '18 15:09 tpinne

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

mikespineu avatar Oct 04 '18 14:10 mikespineu

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

https://github.com/metafizzy/flickity/issues/233#issuecomment-388374039

saltcod avatar Oct 05 '18 12:10 saltcod

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]);

Julix91 avatar Apr 02 '19 19:04 Julix91

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.

codefinite avatar Jun 06 '19 19:06 codefinite

Any update on this?

mrspence avatar Oct 04 '19 11:10 mrspence

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,
      });
    }

bobbaker404 avatar Oct 10 '19 04:10 bobbaker404

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

Hlsgs avatar May 24 '20 14:05 Hlsgs

Any news on this?

zzseba78 avatar Aug 12 '20 19:08 zzseba78

+1

kazunorimiura avatar Sep 15 '20 16:09 kazunorimiura

+1

obliviga avatar Oct 28 '20 23:10 obliviga