Saturday, March 17, 2012

Using FancyBox with Zepto

On the web version of the EatDifferent site, I use the jQuery Fancybox plugin to lightbox meal photos when you click on them. On the mobile version, I left them as clickable images. I've realized that's not a great experience because the user feels like they're leaving the app (because they are!) and on the iPhone, they can't simply press the back button to go back to the app (like you can on Android). So, for the next version of the mobile app, I decided to use lightboxes for the photos there as well.

Then I had to decide how to lightbox them on mobile. I could use FancyBox, use Twitter Bootstrap modals (which I already use on mobile), or find a mobile-optimized lightbox solution. After doing a quick check in the browser to see how FancyBox works at small screen sizes — and seeing it actually works pretty well — I decided to go for it.

But, there's a catch: I use jQuery on the web, and the more lightweight Zepto on mobile, and very often jQuery plugins use obscure jQuery functionality that Zepto does not support, like optional arguments, alternative constructors, and little known methods. So when I want a jQuery plugin to work with Zepto, I either have to modify the plugin code to use the more "standard" functionality or I have to add the extra functionality to my Zepto copy. My preference is not to mess with plugin code, as that makes it hard to upgrade to new plugin versions and its usually difficult to figure out what exactly the plugin author intended. But, sometimes it's easier. In the case of FancyBox, I did a little of both. Here's a run-down:

FancyBox Changes:

You can check out the diff or the full fancybox.js.

  • As usual, I changed the last line of the plugin file to say window.jQuery || window.Zepto, to make it work with either.
  • The plugin used contents(), and I changed it to use children() instead, which seemed to work.
  • The code used a concept called global event triggers in jQuery which lets you trigger an event on no element in particular ($.event.trigger('fancybox-cleanup'). Zepto doesn't have that, so I changed the code to simply trigger on the two elements that listened to that global event.

Zepto Changes:

You can check out the diff or the my full zepto.js. My Zepto is fairly customized for my use (beyond just FancyBox), so you probably don't want to just grab it all.

  • The code used $.support to detect opacity support on browsers. Since I only use Zepto on mobile and it doesn't have the support module, I hard-coded support for it ($.support = {opacity: true};)
  • The code called the append() method with multiple arguments, which Zepto doesn't support out of the box. I found this gist that adds support for it and merged that into my copy.
  • The code used "is:visible" to detect DOM element visibility. Unfortunately, that pseudo-selector was completely made up by jQuery and it won't work in Zepto since it uses the browser's built-in query selector engine. There's a long discussion about how to best generally handle these fake selectors, but in the meantime, I just added a special check to my filter function to just handle is:visible, based on this gist.
  • The code used fadeTo(), fadeIn() and fadeIn(), which aren't in the core Zepto files but are available if you include the fx.js and fx_methods.js files.
  • Unfortunately, the code also used stop() to stop fading animations, which isn't in those optional Zepto files, so I then had to copy the files from this Zepto fork instead.

If this post was of interest to you, you may also be interested in reading my first post on porting most of my plugins to Zepto.

No comments: