Metafizzy socks now on sale

19 Jun 2018 · by David DeSandro

Metafizzy socks

Metafizzy socks

I love socks. Slipping into a pair of socks you adore can change your whole day. No matter how bad things get, at least you're feet are living their best life. That's why I made Metafizzy socks. Wild colors, overly-cute design. These are the socks my feet were made for.

Metafizzy socks feature argyle design on leg and fizzy bear face on the top of the foot. Things are looking up for that cutie. Knit with comfy-soft cotton threads, you'll be walking on sunshine. Colors are a show-stopping combo of red, orange, gold, and blue. 1-size fits all Adult Medium. Made in the USA.

Makes a great gift. Everyone wears socks.

Look at these glorious socks.

Don't see your country as a shipping destination? Email yo@metafizzy.co or Tweet @metafizzyco to get your country added.

Check out the Eventbrite crew rocking the rainbows.

Pre mascot logo

15 Jun 2018 · by David DeSandro

Pre logo mascots

I designed the mascot & logo for Pre, a social app for sharing moments with your best friends.

I explored a variety of concepts in the initial sketches, going for friendly and lightweight.

  • Osprey - It has "pre" in the name
  • Cute, bouncy mammals - Jaguarundi, polecat, and pudΓΊ
  • Piranha - Sort of sounds like "pre": pre-ranha (work with me)
  • Rabbit - A take on the rabbit in the hat from Alice in Wonderland. Trying to meet up.
  • Preeple - abstract humanoid cuties

Pre logo mascot sketches

Sensing a promising lead, I then did a wider exploration of the Preeple concept. I tried out hair shapes, accessories, and more detailed character designs

Pre logo preeple sketches

We selected Osprey 3, Rabbit 24, and Preeple 28 sketches to work on in vector.

Pre logo round2

Several of theses concepts went to live on as logos for Logo Pizza. Osprey and Mechanical hare are available for sale.

Pre Logo Pizza logos

For Pre, we landed on the Preeple 1 design, with a little dude waving to say hello. He perfectly exemplified the character of Pre: friendly, simple, and inviting. From there, I chisled out the finer details of the design.

Pre logo round3

Pre app icon

The 'green waver' was selected as the main logo. We also decided to build out his supporting cast. They represented the other friends in your crew: the selfie queen, the grumpster, and the person who you always have to convince to come out. I also provided multiple facial expressions, which could be used as default avatars or in messaging.

Pre mascot characters

Pre tweet

Alas, the Pre app is likely no longer available, but that lil' dude will keep on waving in my heart.


Interested in a logo for your work? Get in touch! Email yo@metafizzy.co to get started.

Metafizzy logos

Duck-punching the prototype

9 Apr 2018 · by David DeSandro

Every so often I encounter an issue with a Metafizzy plugin that I can't resolve its standard API. The only way to get it done is to overwrite core plugin code. These issues call for duck-punching. Paul Irish's post on duck-punching jQuery is a great overview.

Duck-punching is another name for monkey patching. Monkey patching is a technique to β€œextend or modify the runtime code of dynamic languages without altering the original source code.”

Let's say you want to add a class is-animating to Flickity when it is animating. Flickity does not have a startAnimation event. But it does have a startAnimation method on its prototype. We can duck-punch that method to add our desired behavior.

// get original method
var startAnimation = Flickity.prototype.startAnimation;
// overwrite method
Flickity.prototype.startAnimation = function() {
  // call original method
  startAnimation.apply( this, arguments );
  // do new stuff
  this.element.classList.add('is-animating');
};

Let's break down what's happening here:

  • Store the original method as a variable.
  • Overwrite the method.
  • Within the new method, trigger the original behavior by calling the stored method with apply(). Pass in this and arguments to set instance and any arguments.
  • Add new behavior within the method.

Likewise, we can duck-punch the _create method to add an event listener on settle to remove the class.

// get original method
var _create = Flickity.prototype._create;
// overwrite method
Flickity.prototype._create = function() {
  // call original method
  _create.apply( this, arguments );
  // do new stuff
  this.on( 'settle', function() {
    this.element.classList.remove('is-animating');
  });
};

Here's a demo with these two duck-punches:

View Flickity - duck punches CodePen

By duck-punching prototype methods, we change the behavior of all Flickity instances. If you want to change behavior on individual instances, you can do so by duck-punching instance methods.

// create new instance
var flkty = new Flickity('.carousel');

// get original method
var startAnimation = flkty.startAnimation;
// overwrite method
flkty.startAnimation = function() {
  // call original method
  startAnimation.apply( this, arguments );
  // do new stuff
  console.log('start animation');
};

Duck-punching is a nice way to mess around with Metafizzy plugins without overhauling internal code. It's great for debugging, allowing you to shim in console.log like in the example above.

Duck-punching is one of those quirky features of JavaScript. At first it looks like a gross hack, but its usefulness makes it a thing of ugly beauty.

Windex: pretty-up your localhost

3 Apr 2018 · by David DeSandro

Windex icons

As 90% of my job is making static websites, I am often looking at directory index pages. You know, those ancient pages with titles like Index of /projects/. Long ago, I swore off the ugly unstyled 90's default and chose a higher path.

Windex is a little library that creates styled directory index pages with Apache. Simply put, it allows you to use CSS on your localhost. Features include:

  • SVG icons, sized pixel-perfect for 24x24 & multiples thereof
  • View HTML files without extensions: project/page.html at project/page
  • Nice mobile view with big tap targets

Windex screenshot

View Windex demo on CodePen

Setting up a practical localhost on macOS

I like to use localhost so I can view my projects in ~/projects. This allows me to create static sites that I can easily view in the browser, without having to start up a server. For example, going to http://localhost/masonry/sandbox allows me to view ~/projects/masonry/sandbox.

Below are instructions to set that up on macOS. Sorry Windows users, you're on your own here. This will make a single user's folder viewable for all users. For separate users folders like localhost/~username, view these instructions.


Open /etc/apache2/httpd.conf in your text editor. Making changes to this file will require administrator access. Change the following lines (line numbers may vary in your file):

Line 169: Enable mod_rewrite. This enables .htaccess files to rewrite URLs.

LoadModule rewrite_module libexec/apache2/mod_rewrite.so

Lines 238-239: Change DocumentRoot and subsequent Directory to your desired directory. This sets localhost to point to the directory.

DocumentRoot "/Users/username/projects"
<Directory "/Users/username/projects">

Line 252: Within this <Directory> block, add Indexes to Options. This enables file index view.

    Options FollowSymLinks Multiviews Indexes

Line 260: Change AllowOverride value to All. This enables .htaccess files.

    AllowOverride All

That block should look like:

DocumentRoot "/Users/username/projects"
<Directory "/Users/username/projects">
    # Possible values for the Options directive...
    Options FollowSymLinks Multiviews Indexes
    MultiviewsMatch Any

    # AllowOverride controls what directives...
    AllowOverride All

    # Controls who can get stuff from this server.
    Require all granted
</Directory>

In Terminal, start or restart apachectl.

sudo apachectl restart

View http://localhost in a browser. You'll should see the index page for ~/projects. Without Windex, it's ugly, but it works. With Windex, it's pretty.

If you messed up httpd.conf, you can replace it with its original at /etc/apache2/original/httpd.conf.

Viewing on other devices on macOS

You can view this localhost on another device like a phone.

  1. Open System Preferences. Select Sharing.
  2. Change Computer name to something rad, like thunderclaw, if you haven't already.
  3. Enable File sharing.

Now, you can view http://thunderclaw.local on another device if you are on the same Wi-Fi.


Windex is the one tool I use every day. Originally created in 2010, it is one of my first open-source projects. Before this recent overhaul, I had been using that first version for years, which had iPhone v1 glossy buttons. Let's see if this version lasts me until 2026.

What is `this` in event listeners?

29 Mar 2018 · by David DeSandro

I was just asked:

Hello desandro! I'm a new developer.

elem.addEventListener( startEvent, this );

I wonder what is this means. I'm confused. [edited]

Confusing, indeed! this in JavaScript is difficult to learn, and especially tricky to understand when used in event listeners.

Let's take a step back and look at a simplified example. Here is a demo for a mouse-draggable element.

var dragElem = document.querySelector('.draggable');

var x = 0;
var y = 0;
var dragStartX, dragStartY, pointerDownX, pointerDownY;

dragElem.addEventListener( 'mousedown', function( event ) {
  // keep track of start positions
  dragStartX = x;
  dragStartY = y;
  pointerDownX = event.pageX;
  pointerDownY = event.pageY;
  // add move & up events
  window.addEventListener( 'mousemove', onmousemove );
  window.addEventListener( 'mouseup', onmouseup );
});

function onmousemove( event ) {
  // how much has moved
  var moveX = event.pageX - pointerDownX;
  var moveY = event.pageY - pointerDownY;
  // add movement to position
  x = dragStartX + moveX;
  y = dragStartY + moveY;
  // position element
  dragElem.style.left = x + 'px';
  dragElem.style.top = y + 'px';
}

function onmouseup() {
  // remove move & up events
  window.removeEventListener( 'mousemove', onmousemove );
  window.removeEventListener( 'mouseup', onmouseup );
}

See the Pen Draggable, single element by Dave DeSandro (@desandro) on CodePen.

To start dragging, I first add a mousedown event listener. When triggered, I then add listeners for mousemove and mouseup. In onmousemove is where I calculate and set the element's position. In onmouseup, I remove mousemove and mouseup listeners to stop dragging.

This works just fine for a single element. Metafizzy plugins are designed to handle multiple instances on the same page.

var dragElems = document.querySelectorAll('.draggable');
for ( var i=0; i < dragElems.length; i++ ) {
  var dragElem = dragElems[i];
  // now what?...
}

One way to approach this is to wrap up the draggable code into its own big function and call that for each element. But I like to use classes with prototype to handle multiple instances of the same behavior.

// Dragger class
function Dragger( element ) {
  this.element = element;
  this.x = 0;
  this.y = 0;
  // doesn't work!
  this.element.addEventListener( 'mousedown', this.onmousedown );
}

Dragger.prototype.onmousedown = function( event ) {
  this.dragStartX = this.x;
  this.dragStartY = this.y;
  this.pointerDownX = event.pageX;
  this.pointerDownY = event.pageY;
  // doesn't work, again!
  window.addEventListener( 'mousemove', this.onmousemove );
  window.addEventListener( 'mouseup', this.onmouseup );
};

Now I need to add an event listener within the Dragger class functions. But there is a problem. I want to use this to reference the instance of the Dragger class within the event handler functions. But functions added with addEventListener will reference the bound element as this, not the function or object.

dragElem.addEventListener( 'mousedown', function() {
  console.log( this );
  // => Element
});

handleEvent

One solution is to use a little-known feature of browser JavaScript, handleEvent. An object with an handleEvent method will be triggered with when added with addEventListener. Within the handleEvent method, this will refer to the listener object, not the element.

var listener = {
  greeting: 'Hello ',
  handleEvent: function( event ) {
    console.log( this.greeting + event.type );
  },
};

dragElem.addEventListener( 'mousedown', listener );
// on mousedown...
// => 'Hello mousedown'

See simple handleEvent demo on CodePen.

The handleEvent method can be used for multiple events. You can specify logic by using event.type.

var listener = {
  greeting: 'Hello ',
  handleEvent: function( event ) {
    console.log( this.greeting + event.type );
  },
};

dragElem.addEventListener( 'mousedown', listener );
dragElem.addEventListener( 'mousemove', listener );
dragElem.addEventListener( 'mouseup', listener );
// on mousedown => 'Hello mousedown'
// on mousemove => 'Hello mousemove'
// on mouseup => 'Hello mouseup'

Back to the Dragger class. So now I add the handleEvent method to cooridate which event method to trigger. Then I can add this as the event listener.

// Dragger class
function Dragger( element ) {
  this.element = element;
  this.x = 0;
  this.y = 0;
  // add this as event listener, trigger handleEvent
  this.element.addEventListener( 'mousedown', this );
}

// trigger .ontype from event.type
// i.e. trigger onmousedown() from mousedown
Dragger.prototype.handleEvent = function( event ) {
  var method = 'on' + event.type;
  // call method if there
  if ( this[ method ] ) {
    this[ method ]( event );
  }
};

Dragger.prototype.onmousedown = function( event ) {
  this.dragStartX = this.x;
  this.dragStartY = this.y;
  this.pointerDownX = event.pageX;
  this.pointerDownY = event.pageY;
  // add this as event listener, trigger handleEvent
  window.addEventListener( 'mousemove', this );
  window.addEventListener( 'mouseup', this );
};

See the Pen Draggable event listeners, handleEvent by Dave DeSandro (@desandro) on CodePen.

So to the original question: this can be used as an event listener as it has a handleEvent method. That method then triggers other methods that match event.type, like onmousedown.

I learned the handleEvent technique back in 2010. I use it in all the Metafizzy plugins. But JavaScript has come a long way in that time.

bind this

Instead of adding circuitry through handleEvent, you can specify this with .bind().

The Function.prototype.bind() method lets you specify the value that should be used as this for all calls to a given function.

Using bind() has an additional benefit in that you can add multiple event listeners for the same event name.

this.element.addEventListener( 'click',
  this.onElementClick.bind( this ) );

this.button.addEventListener( 'click',
  this.onButtonClick.bind( this ) );

But, because bind() returns a new function, you will need to keep track of that function if you want to remove it later.

this.handleElementClick = this.onElementClick.bind( this );
this.handleButtonClick = this.onButtonClick.bind( this );
// add event listener
this.element.addEventListener( 'click', this.handleElementClick );
this.button.addEventListener( 'click', this.handleButtonClick );
// remove event listener
this.element.removeEventListener( 'click', this.handleElementClick );
this.button.removeEventListener( 'click', this.handleButtonClick );

Here's what the Dragger class looks like using bind().

function Dragger( element ) {
  this.element = element;
  this.x = 0;
  this.y = 0;
  // bind self for event handlers
  this.mousedownHandler = this.onmousedown.bind( this );
  this.mousemoveHandler = this.onmousemove.bind( this );
  this.mouseupHandler = this.onmouseup.bind( this );

  this.element.addEventListener( 'mousedown', this.mousedownHandler );
}

Dragger.prototype.onmousedown = function( event ) {
  this.dragStartX = this.x;
  this.dragStartY = this.y;
  this.pointerDownX = event.pageX;
  this.pointerDownY = event.pageY;

  window.addEventListener( 'mousemove', this.mousemoveHandler );
  window.addEventListener( 'mouseup', this.mouseupHandler );
};

See the Pen Draggable bind this by Dave DeSandro (@desandro) on CodePen.

Arrow functions

With the new ES6 hotness, you can specify this using arrow functions. Within an arrow function, this will refer to the enclosing object.

function Dragger( element ) {
  this.element = element;
  this.x = 0;
  this.y = 0;

  // event listeners, with arrow functions
  this.onmousedown = ( event ) => {
    this.dragStartX = this.x;
    this.dragStartY = this.y;
    this.pointerDownX = event.pageX;
    this.pointerDownY = event.pageY;

    window.addEventListener( 'mousemove', this.onmousemove );
    window.addEventListener( 'mouseup', this.onmouseup );
  };

  this.onmousemove = ( event ) => {
    var moveX = event.pageX - this.pointerDownX;
    var moveY = event.pageY - this.pointerDownY;
    this.x = this.dragStartX + moveX;
    this.y = this.dragStartY + moveY;
    this.element.style.left = this.x + 'px';
    this.element.style.top = this.y + 'px';
  };

  this.onmouseup = () => {
    window.removeEventListener( 'mousemove', this.onmousemove );
    window.removeEventListener( 'mouseup', this.onmouseup );
  };

  // add event listener
  this.element.addEventListener( 'mousedown', this.onmousedown );
}

See the Pen Draggable event listeners, arrow functions by Dave DeSandro (@desandro) on CodePen.

Personally, I'm not a fan of the arrow function technique for this scenario. It puts the method code inside another function, rather than outside on prototype. But I'm including it for completeness.

Each technique has its own pros and cons. handleEvent has served me well over the years, but I'm finding that I run into event name conflicts with big plugins like Flickity. So I'm starting to use out bind() a bit more. But then I miss the elegance of adding just this and not having to deal with extra event handler functions. Arrow functions, meanwhile, are just not for me.

Flickity v2.1: fullscreen, hash, & more

23 Mar 2018 · by David DeSandro

Flickity

Happy birthday Flickity! 3 years old! Flickity has grown to be Metafizzy's second most popular library. As such, it deserved some new goodies in a big new release, v2.1.

Fullscreen

View Flickity fullscreen by enabling fullscreen. Click the fullscreen button to resize the carousel to take up the entire browser window. Now you don't need a separate library to zoom images. ENHANCE.

Hit ESC to exit fullscreen. Flickity fullscreen comes with its own API with viewFullscreen and exitFullscreen methods.

This feature was a long time coming. It was first requested while Flickity was still in beta. The issue gained close to a hundred πŸ‘ over the years. I'm delighted to finally ship it.

URL hash

Select slides with URLs with hash. This works both ways: clicking on hash links like <a href="#carousel-cell2"> will select matching the #carousel-cell2 element, and selecting a new slide will change the page URL to the selected cell's id. So you can share URLs to specific slides.

Both fullscreen and hash are add-on features. They are maintained as separate projects outside of Flickity, so the Flickity source code doesn't get bloated with new features. You can choose to add these features by adding their scripts to your project. See flickity-fullscreen and flickity-hash on GitHub.

lazyLoad srcset

Lazy-load images with srcset by setting the data-flickity-lazyload-srcset attribute. You can also set data-flickity-lazyload-src as well to set src as a fallback.

<img data-flickity-lazyload-srcset="
    walrus-large.jpg 720w,
    walrus-med.jpg 360w"
  sizes="(min-width: 1024px) 720px, 360px"
  data-flickity-lazyload-src="walrus-large.jpg"
  />

change event

Flickity's select event is triggered any time a slide is selected. But it will trigger even if the same slide is selected. Flickity v2.1 now has the change event, which will only be triggered when the selected slide has changed.

$carousel.on( 'change.flickity', function( event, index ) {
  console.log( 'Slide changed to ' + index )
});

And more!

  • Added draggable: '>1' option setting (now default) to disable dragging if only one slide.
  • Added ready event. Added on option to capture initial events.
  • Enabled staticClick event when not draggable.
  • Bug fixes with prepend, remove, and wrapAround.

Read the v2.1.0 release notes for details.

With these features, Flickity is now perfect... Naw. You can continue to help me select new features by looking over requested features on the GitHub issue tracker and adding your πŸ‘ reaction.