Setting JavaScript functionality with CSS

12 Jan 2015 · by David DeSandro

One of Slick's hotter feature request is to disable Slick functionality at certain CSS breakpoints. This touches at a tricky issue: setting JavaScript functionality with CSS. Imagine this CSS pseudo-code:

/* default css, small devices like phones, less than 768px */

/* JS widget is enabled by default */

@media screen and ( min-width: 768px ) {
  /* larger devices: tablets and up */

  /* JS widget is now disabled */
}

Slick has an awesome feature where you can set variations of options for various breakpoints.

$('.responsive').slick({
  slidesToShow: 4,
  slidesToScroll: 4,
  responsive: [
    {
      breakpoint: 1024,
      settings: {
        slidesToShow: 3,
        slidesToScroll: 3
      }
    },
    {
      breakpoint: 600,
      settings: {
        slidesToShow: 2,
        slidesToScroll: 2
      }
    }
  ]
});

But this approach comes with issues. It works by setting style information in JavaScript, when it should be kept in CSS. Also, JavaScript can mis-interpret the window width, causing a mismatch between the breakpoint used in JavaScript versus CSS.

Isotope element sizing

With Isotope, I tried a technique to set options via the state of CSS. You can set the columnWidth by pointing to the size of a dummy element.

<div class="container">
  <!-- dummy element for size of columnWidth -->
  <div class="grid-sizer"></div>
  <div class="item"></div>
  <div class="item"></div>
  ...
</div>
/* default, 3 columns */
.grid-sizer, .item { width: 33.333%; }

@media screen and ( min-width: 768px ) {
  /* larger devices, 5 columns */
  .grid-sizer, .item { width: 20%; }
}
$('.container').isotope({
  itemSelector: '.item',
  masonry: {
    // set columnWidth by width of .grid-sizer
    columnWidth: '.grid-sizer'
  }
});

See the Pen Isotope - element sizing columnWidth by David DeSandro (@desandro) on CodePen.

This technique requires an extra element be added to the HTML, but it allows the styles to be kept in CSS. So if the user changes the breakpoint or the size of the columns, they only need to change it in one place.

Activating Flickity with conditional CSS

Back to the first problem: How can the widget be enabled or disabled with CSS? I could use a dummy element, like with Isotope, but I'd rather not require extra markup if it's not necessary.

I like Jeremy Keith's Conditional CSS technique. It works by reading the content of a pseudo-element :after.

@media all and (min-width: 45em) {
  body:after {
    content: 'widescreen';
    display: none;
  }
}
var size = getComputedStyle( document.body,':after' ).content;
if ( size.indexOf('widescreen') !=-1 ) {
    // do widescreen stuff
}

Try resizing the window on this CodePen to see this technique in effect.

This technique has been built into Flickity. I've added a watch option. It looks for :after content of its container element to be flickity. If so, Flickity is enabled. If not, Flickity is disabled.

See the Pen Flickity watch option by David DeSandro (@desandro) on CodePen.

Technically, Flickity is always 'on'. But when inactive, its only functionality is to watch and see if it should activate. I added extra logic so that Flickity can activate and deactivate itself (rather than destroy/create) so it can re-use the same instance and UI elements.

Fallbacks

This technique is not without its own challenges. It doesn't work in IE8, Android 2.3, and previous versions of Chrome. My current solution is to run a feature test if reading the :after content will work. If not, the user can set watch: 'fallbackOn' to enableFlickity in these cases, or stick with watch: true to disable them. There's also a possible collision with how .clearfix uses :after. After Flickity has been released and I get more input, this feature is likely to be revisited.

Previously

If you're just joining us, I'm making a new gallery library! The story thus far...

Flickity is up on GitHub. Follow along development!