imagesLoaded v5 released

22 Feb 2022 · by David DeSandro

imagesloaded v5

Ladies & gentleman of the jury, if it may please your honor, I give you a new major version of imagesLoaded, v5. imagesLoaded is a small-ish JavaScript plugin to detect when images have been loaded on the page. v5 brings some hotly requested features:

  • Added proper support for srcset
  • Added proper support for <picture>
  • Refactored with ES2018
  • Added support for crossOrigin attribute
  • BREAKING - dropped support for Internet Explorer

The srcset and <picture> support have been a long time coming. You can now use imagesLoaded with modern image markup.

imagesLoaded plays a key role with Masonry, Isotope, and Flickity. In order to layout elements with JavaScript, you need to know the element's size. And to measure the element's size, you need to know when images are loaded. imagesLoaded solves for that. It's been instrumental to the success of Metafizzy's plugins.

imagesLoaded first started a gist by Paul Irish. It's now 12 years old.

See imagesloaded.desandro.com to complete documentation.

Draggabilly v3.0.0 released

29 Dec 2021 · by David DeSandro

Today, I released a new major version of Draggabilly, the draggable vanilla JS library, v3.0.0. Browser support for Internet Explorer has been dropped. JavaScript code has been written with ES2018 features. draggabilly.pkgd.js is now 500 lines-of-code smaller. Changes include:

  • โŒ BREAKING - Dropped AMD / RequireJS support
  • โŒ BREAKING - Dropped IE10 & IE11 support
  • ๐Ÿ›  Updated JS code to ES2018.
  • ๐Ÿ“ฆ Used Unidragger v3. Removed Unipointer
  • ๐Ÿ”” Set handle to Element or Array or NodeList.
  • ๐Ÿž Allowed to scroll on element when dragging is disabled. Fixed bug #189
  • ๐Ÿ›  Checked for window for server-size rendering environments
  • ๐Ÿ›  Allowed to trigger dragEnd to stop dragging. #177

If you use Draggabilly, please update and report back if you run into any issues. If you don't use Draggabilly โ€” give it a whirl!

I've been using the holiday break to dip back in to Metafizzy. This work on Draggabilly paves the road for a similar major version update to Flickity.

Infinite Scroll v4 released

11 Jan 2021 · by David DeSandro

Infinite Scroll v4 screenshot

This week, I released the new major version of Infinite Scroll, v4. New features include:

Give Infinite Scroll v4 a look right here.

Infinite Scroll v4 is largely backwards compatible with v3. Upgrading to v4 is no sweat. Breaking changes include:

  • replaced responseType option with responseBody. This effects loading JSON.
  • Internet Explorer and Android 4 support dropped.
  • Removed RequireJS support and AMD export.

A lot of work went into improving the infrastructure around around the plugin. JS code is linted with ESLint. Testing is now done via command line with Puppeteer. Both these improvements allow for automated testing with GitHub Actions, allowing for integrated testing on commits and Pull Requests (take a look at the workflows/nodejs.yml).

Infinite Scroll is the oldest piece of software I support, dating back to Paul Irishโ€™s original release in 2008. Not many software companies can boast that kind of longevity. Iโ€™m proud to be able to keep it going all these years.

I chose to upgrade Infinite Scroll as it is less popular, making it a good testing ground for new development. Iโ€™ve laid the groundwork to start tackling the bigger plugins next: Isotope & Flickity.

Can't mix-in ES6 class expressions

22 Dec 2020 · by David DeSandro

Here's a quick JavaScript bug story. I'm working on Infinite Scroll v4, updating older ES5 code to ES2018.

If you create a class using the traditional function class definition, you can use mix it in to another class' prototype with Object.assign(). I use this pattern with EvEmitter and my plugins.

// function class definition
function EvEmitter() {}

// prototype methods
EvEmitter.prototype.emit = function() {};
EvEmitter.prototype.on = function() {};
EvEmitter.prototype.off = function() {};

// plugin function class
function InfiniteScroll() {}

// mixin EvEmitter prototype into Infinite Scroll
Object.assign( InfiniteScroll.prototype, EvEmitter.prototype );

// now InfiniteScroll can use EvEmitter methods
InfiniteScroll.prototype.create = function() {
  this.emit( 'load', function() {} );
  this.on( 'request', function() {} );
};

But, if you use ES6 classes expressions, you can no longer use the Object.assign() mix-in pattern.

// class expression
class EvEmitter {
  // prototype methods
  emit() {}
  on() {}
  off() {}
}

function InfiniteScroll() {}

// mixin
Object.assign( InfiniteScroll.prototype, EvEmitter.prototype );

InfiniteScroll.prototype.create = function() {
  // Uncaught TypeError: this.emit is not a function
  this.emit( 'load', function() {} );
  this.on( 'request', function() {} );
};

Whaaa? The core issue is that class methods are non-enumerable. If you try iterating over Class.prototype that was set with a class expression, you'll get nothing.

class EvEmitter {
  emit() {}
  on() {}
  off() {}
}

console.log( Object.keys( EvEmitter.prototype ) );
// => []
// empty!

I suppose this is an improvement

Thatโ€™s good, because if we for..in over an object, we usually donโ€™t want its class methods.

Except I've been iterating over prototype for years.

The obvious solution is to define the inherited class with a class expression as well, using extend to inherit the superclass.

// okay, this works
class InfiniteScroll extends EvEmitter {
  create() {
    this.emit( 'load', function() {} );
    this.on( 'request', function() {} );
  }
}

But it's a bummer that I lose a feature by opting-in to the new syntax. EvEmitter really is a mix-in and I would like to be able to use it like one. Read Angus Croll about why mixins are a good JavaScript pattern.. There is an ES6 approach for mix-ins and class expressions, but I'm not a fan.

So I reverted using class and switched back to the original function expression & prototype setting.

function EvEmitter() {}

EvEmitter.prototype.emit = function() {};
EvEmitter.prototype.on = function() {};
EvEmitter.prototype.off = function() {};

YAY back in business.

Full-time fizzy finish

19 Mar 2019 · by David DeSandro

New job at Compass

After four years of being my full-time gig, Metafizzy is reverting back to a side-business. This week I start a new full-time job at Compass.

For my personal career and well-being, the new job is a huge win. I'll be working on a bigger endeavor, with world-class colleagues, producing a reliable income with great benefits. I'm excited for this next chapter.

For Metafizzy, it's a bittersweet coda. Over the past two years, revenue had plateaued. Being a father and mortgage-owner, the risk of staying independent was too high. I welcome the stability of joining a thriving company and being adequately compensated for my effort.

My four years full-time on Metafizzy is the longest tenure with a single employer of my entire career. I feel lucky to have gotten this far, but also a bit heart-broken that it's over. I think of all the things I never got to do: hiring teammates, championing a business model, shipping more products. There is much left undone.

But there's also so much left that still works. Metafizzy, its plugins, and its brand remains completely in my possession. Metafizzy never took outside funding. I have no lingering debt that requires a hard exit. Everything continues to live on.

In practical terms, that means plugins will continue to be supported. The plugins' code bases have long been stable for years. (Isotope, for example, hasn't required a minor version update since 2015.) I do have plans for the next major version updates, dropping IE11 and fully adopting ES6, but that's still further down the road.

Outside of the plugins, everything else can be considered on hiatus. That covers freelance logo design and front-end development, Logo Pizza, and Fizzy School. I may dip back into these projects, but don't hold your breath.

I still feel there is so much money to be made in independent front-end development. Even though our practice has largely been free to learn and free to use, the value that front-end developers drive is enormous. The Metafizzy model was on to something and that something is still out there.

Fade with Flickity v2.2.0

31 Jan 2019 · by David DeSandro

Flickity v2.2.0 has been released with all-new fade feature. The fade option allows you to fade between transitioning slides instead of moving.

Flickity fade

I built out this feature as separate add-on package flickity-fade. Read more about Flickity's modular architecture here. Fade works with dragging, groupCells, wrapAround, imagesLoaded, and everything else in the Flickity feature-set.

To use fade, add flickity-fade.css to your stylesheets, flickity-fade.js to your scripts, and then enable fade in your Flickity options.

$('.carousel').flickity({
  fade: true,
});

Flickity fade was four years in the making. I held off for so long because dragging & moving is both obvious and what makes Flickity special. But after 4 years of requests, I relented. Lords of UX, forgive me.


Flickity v2.2.0 also comes with other smaller features and bug fixes:

  • Set the initial selected cell that matches a selector string with initialIndex: '.selector'
  • Better accessibility support with aria-hidden
  • The tap-listener package was removed as a dependency, shedding some code weight
  • Fixed triggering events after destroy
  • Fixed iOS 9 dragging bug

Most of these improvements originated from open-source contributions. View the v2.2.0 release notes for the original GitHub issues and Pull Requests.


Flickity turns 4 years old next month. It's been great to see Flickity continue to grow and be used out in the wild. Fading was definitely not my cup of tea, but I was convinced to pursue it after witnessing sustained interest over years. People didn't just want fading, they wanted all of Flickity's features plus fading. My heart swells.