Flickity v1.1.1

2 Sep 2015 · by David DeSandro

Flickity's latest patch release will make your fingers happy. Flickity v1.1.1 fixes several touch device bug. Let's investigate!

Fixed freeScroll bug flicking past ends

The issue: #181. With freeScroll, with a big flick, you could flick the slider completely out of the carousel.

The fix: This bug was a regression — it didn't occur in previous versions. It was caused by using a different calculation for resting position after a flick. In an earlier change, I over-thought a solution and 'fixed' a problem that actually wasn't occurring. Reverting to the previous calculation fixed it.

Learned: The real trouble is that behavior features like flicking and dragging are difficult to reliably test. I have implemented some tests that fake these behaviors, but they don't always pass test 100% of the time. When I introduced this bug, the tests either didn't catch it, or I didn't have a test for it.

Fixed prepend animation regression

The issue: #201. Using prepend or insert would cause the slider to animate. Another regression.

The fix: Set the position after inserting cells.

Learned: This regression was introduced with lazyLoad. I changed the behavior when adding or changing cell size. But I didn't think to see how this change affected prepend/insert.

Allow range input sliding

The issue: #216. Range type <input>s would not be able to slide within a Flickity cell.

The fix: Add logic so that any pointer on a range only works on the range, and does not trigger any Flickity behavior. While you can drag a Flickity slider by tapping down a button, or a text input, you cannot by tapping down on a range.

Learned: Sliders in sliders happen.

See the Pen Flickity - Range Input Test by Praveen Puglia (@praveenpuglia) on CodePen.

Fix asNavFor double movement bug in iOS

The issue: #189. With iOS, tapping on a nav gallery using asNavFor would cause it to shift once then again.

Learned: The bug was caused by iOS emulated click event. iOS and older versions of Chrome for Android fake a click event 300 millisecond after a user taps on the screen (See also Taps are faster than clicks). Typically, this click event is harmless, as the earlier tap event works as well on Flickity. But with the asNavFor, it was causing a tap/click to occur on two different cells. The behavior worked something like this:

  1. Tap on the cell to move
  2. Nav gallery shifts to new cell
  3. Fake click is triggered on new cell where previous cell was
  4. Nav gallery shifts again

The fix: I added timing logic to listen for these fake click events that happen after touch events. It's a bit of a hack, but it does the job. No double taps.

Fixed staticClick triggering after scroll on touch devices

The issue: Flickity's staticClick event was being triggered after scrolling the page on a touch device.

The fix: After pressing down, listen for scroll events. If page scrolls a bit, cancel the click behavior.

Learned: Touch devices do this as a standard behavior as part of their OS. You can press down on a button, and see that button activated. Once you start scrolling the page, the button is deactivated.


Several of these bugs were identified and reported by Flickity's keen-eyed user-base. Help make Flickity better by reporting any bugs you find. Or star Flickity on GitHub because baby you're a firework.

Triggering jQuery and vanilla JS events

3 Jul 2015 · by David DeSandro

You can now bind to jQuery events in Isotope, Packery, and Masonry. The recent upgrades allow you to use standard jQuery event methods .on(), .off, and .one(), rather than using ugly plugin method syntax.

// previous plugin method syntax
// Isotope <= v2.2.0
$grid.isotope( 'on', 'layoutComplete', function() {...})

// standard jQuery event
// Isotope >= v2.2.1
$grid.on( 'layoutComplete', function() {...})

View Isotope layoutComplete demo on CodePen.

This feature is already in Flickity and Draggabilly. It was prime time to port it over to the layout libraries.

jQuery plugin events

Events are a great API design pattern to add to any library. They enable developers to build functionality on top of a library, without having to add lots of code into a big config object. Take a look at this Draggabilly demo, which disables other draggables so that only one can be dragged at a time.

var $draggable = $('.draggable').draggabilly();

// disable other draggabillies on dragStart
$draggable.on( 'dragStart', function( event ) {
  $draggable.filter( function( i, elem ) {
    return elem != event.target;
  }).draggabilly('disable');
});
// re-enable on dragEnd
$draggable.on( 'dragEnd', function() {
  $draggable.draggabilly('enable');
});

Unfortunately, events are often missing from jQuery plugins. Many plugins stick to the config object pattern for this kind of logic. Here's that same code block re-written using a config object.

var $draggable = $('.draggable').draggabilly({
  onDragStart: function( event ) {
    $draggable.filter( function( i, elem ) {
      return elem != event.target;
    }).draggabilly('disable');
  },
  onDragEnd: function() {
    $draggable.draggabilly('enable');
  }
});

While this code is more compact, it comes at a cost. Config objects make sense for settings. They align well with novice developers thinking declaratively, like writing HTML and CSS. But events are not settings. Events can be turned on and off asychronously. The same event can have multiple listeners. Events can require programmatic thinking. Using events and listeners can open up how to think like a programmer.

Consider a scenario where I want to bind to an event after one event has happened, or a scenario when I want to unbind an event.

$draggable.on( 'dragStart', function() {
  // listen to dragEnd after dragStart
  $draggable.on( 'dragEnd', onDragEnd );
});

function onDragEnd() {
  // on dragEnd after dragStart logic...
  // unbind listener
  $draggable.off( 'dragEnd', onDragEnd )
}

Re-writing this code with a config object pattern would look ugly. Using the event pattern makes for less complex code that's easier to follow. It exposes a different kind of programmatic logic that's not possible with declarative config objects.

dispatchEvent method

Looking under the hood, triggering jQuery and vanilla JS events is handled by the dispatchEvent method:

/**
 * emits events via eventEmitter and jQuery events
 * @param {String} type - name of event
 * @param {Event} event - original event
 * @param {Array} args - extra arguments
 */
Widget.prototype.dispatchEvent = function( type, event, args ) {
  // add original event to arguments
  var emitArgs = event ? [ event ].concat( args ) : args;
  // trigger vanilla JS event
  this.emitEvent( type, emitArgs );
  // trigger jQuery event
  if ( jQuery && this.$element ) {
    if ( event ) {
      // create jQuery event
      var $event = jQuery.Event( event );
      $event.type = type;
      this.$element.trigger( $event, args );
    } else {
      // just trigger with type if no event available
      this.$element.trigger( type, args );
    }
  }
};

dispatchEvent emits an event with EventEmitter for vanilla JS events, and creates and triggers a jQuery event. dispatchEvent is then used in the widget's logic:

// with Event object
this.dispatchEvent( 'eventName', event, [ arg1, arg2 ] );
// no Event object
this.dispatchEvent( 'eventName', null, [ arg1, arg2 ] );

Developers can then bind listeners with jQuery or vanilla JS.

// jQuery
$elem.on( 'eventName', function( event, arg1, arg2 ) {
  //...
})

// vanilla JS
widget.on( 'eventName', function( event, arg1, arg2 ) {
  //...
})

Adding jQuery events is a small feature, but it allows developers to re-use code patterns they are familiar with, which makes Metafizzy libraries more approachable. Compared to the previous syntax, a novice developer can look over event demo code and think "Yeah, I can do that."

Dropping IE8 and 9 support

1 Jul 2015 · by David DeSandro

2015 will be the last year Metafizzy supports IE8 and 9. In 2016, we'll release new major versions of our libraries that remove support for the Internet Explorers of yore.

The time has come. Global browser usage is especially low for both browsers. IE8 is at 2%. IE9 is at 1.5%.

IE8 and 9 were the last browsers to have significant feature gaps. Dropping their support will allow a lot of ugly code to be removed. Polyfill libraries can be removed like eventie, classie, and doc-ready.

If you still require IE8 and 9 support, previous versions will still be completely available to download and view documentation. The old versions will no longer get bug fixes or improved features, but you can continue using them as long as you like.

We've opened issues to track this change for each library. Follow along:

Flickity v1.1: lazyLoad and arrowShape

19 Jun 2015 · by David DeSandro

Flickity get its first minor release with v1.1. Just in time for summer with Metafizzy's hottest requested feature.

lazyLoad

lazyLoad works by loading images when a cell is selected.

<!-- set image's URL to load with data-flickity-lazyload -->
<img data-flickity-lazyload="full.jpg" />

Set lazyLoad: true in the Flickity options.

See the Pen Flickity - lazyLoad by David DeSandro (@desandro) on CodePen.

Set lazyLoad to a number to load images in adjacent cells. lazyLoad: 2 will load images in the selected cell, the next 2 cells and the previous 2 cells.

See the Pen Flickity - lazyLoad: 2 adjacent by David DeSandro (@desandro) on CodePen.

arrowShape

Flickity v1.1 brings the new arrowShape option. arrowShape allows you to set a custom shape for the arrow in the previous & next buttons. This is a neat little feature — it doesn't do much, but it's fun to play around with. I even created a WYSIWYG so you can drag around the shape points.

See the Pen Flickity - arrowShape by David DeSandro (@desandro) on CodePen.

40%-off sale

With the new release, Commercial Licenses are on sale 40% off! Save $10, $45, or up to $115 on a license during the sale this week.

Now is a great time to try Flickity. Get to it!

A demo for every thing

2 Jun 2015 · by David DeSandro

I'm going gangbusters with demos. For every option, method, and idea within a Metafizzy library, expect a demo — both in the docs and on CodePen.

UI design deals with visuals and behaviors — things that are difficult to describe in words. A demo is the best way to communicate these concepts.

Creating a demo for every concept is tedious. But each one serves as a focused showcase for an individual behavior. Take for example this demo, which is for Flickity's contain feature.

See the Pen Flickity - contain by David DeSandro (@desandro) on CodePen.

The demo itself is unimpressive, but it clearly shows how the feature works.


Demos enable "hands on" learning. They are playpens that already have some building blocks stacked up. Users can try adding their own blocks, or knocking them all over. By giving the tools to the users, they can investigate and understand it for themselves.

My favorite benefit is how demo coverage aids issues. Since implementing the demos in Masonry and Isotope a year ago, I've found that fewer issues are submitted on GitHub. Masonry has 8,000 GitHub stars, hundreds of daily npm downloads, but less than 20 open issues.

When an issue does get submitted, it's more likely to have a reduced test case with it. Users have to do less work to create reduced test cases — just fork the CodePen. I feel more justified asking for reduced test cases. Can't bother forking a CodePen? Then it's not a real issue.

Demos are the critical thinking tool for front-end developers. They are how developers learn ideas, and how developers verify ideas. By covering your documentation with demos, you better enable your users to understand your product and better enable yourself to defend your own time.

Flickity v1 released

26 Feb 2015 · by David DeSandro

Flickity v1.0.0 is out and ready for prime-time.

Flickity homepage

Check out the gif.

Flickity animated illustration

How much does a widget cost?

Now that it's been properly launched, it's worth considering what it cost to put it all together. In the development of Flickity, I have produced:

Flickity's total development time, from initial demos to v1.0.0 release, has taken about 3 months. Creating Isotope and Packery took about 3 months as well, although I wasn't working on them full-time. Estimating that a front-end developer's yearly salary is $100,000 (which is on the low end for NYC or San Francisco), 3 months would cost $25,000.

A widget costs 25 grand. That's to say, a full-featured widget with all the bells and whistles like Flickity costs 25 grand.

This may sound extravagant. But consider that if for each of Flickity's thousand GitHub stars, it saves one person one hour of work, that would be 1,000 hours. If the average hourly rate for each person is $50:

1,000 hrs × $50/hr = $50,000 total value

I could argue that work put into Flickity costs half of what it's worth. When you consider the aggregate benefits that they can provide, the value of widgets is tremendous.

Previously

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

Flickity v1 is out. Give 'er a flick!