An exquisite jQuery plugin for magical layouts that won't crash your browser
JavaScript Shell
Pull request Compare This branch is 33 commits ahead, 236 commits behind metafizzy:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Isotope (leak free)

Isotope is a cool jQuery plugin that uses lots of other peoples' code to do lots of fancy animated arrangement, filtering and sorting. Unfortunately, it also leaks memory like a fucking sieve, and its author has expressed a complete lack of interest in remediating this situation despite having already authored a fix, so here's a fork for people who will get fired if they let their users' browsers crash for the sake of eye candy because they're not paid to build standalone corporate vanity pages for ad agencies.

The Problem

In a word: Expandos. Despite being the scourge of all web programming for many a year (remember why everyone ran away from Prototype screaming?), they're occasionally necessary in cases where you really, really need persistent data attached to a specific DOM object. Isotope certainly requires this for much of its state awareness, so it keeps track of lists of atoms--which are basically cutesy synonyms for DOM objects.

Where Isotope goes horribly wrong is in committing a textbook JavaScript sin by assigning the results of jQuery selector operations (specifically not and filter) directly to these expando lists. Every jQuery selector operation returns an array with an extra prevObject attribute. This attribute contains the complete result set of the selector--including the actual DOM objects selected--thusly supplying the magic necessary for its amazingly popular chaining mechanism to work.

Check it out on your console:


One of many offenses littered throughout the Isotope code would look like:

this.$allAtoms = this.$allAtoms.not( $content );

Let's think about what that means for a minute. We've got ourselves an Isotope-ified DOM object--for all intents and purposes: this--and it's got some expando data attached to it called $allAtoms, which is a list of all of this's child nodes that Isotope has installed itself. We're running $allAtoms through not in order to filter something undesireable out of it and re-assigning the result of that directly to $allAtoms.

The result of that not contains another prevObject attribute, which contains the result of jQuery's selection efforts. Again, that will be an array of child nodes of this in every case, so this will be retaining nested chains of cyclical references to those elements no matter how many times they're removed.


But it gets worse because this re-filtering and re-assignment process happens repeatedly and the cyclical references they produce predictably snowball. After just a few Isotope insertion/removal cycles, you'll have


and so on and so forth. Don't believe me? Ask Chrome's heap inspector:


Every one of those detached elements has outlived its usefulness and will never be seen again, but since all of their prevObjects contain references to every single DOM element that's ever been inserted into the Isotope container, and each one of them refers to yet another incarnation of the same array, those zombie elements will never be freed. Each part of the whole contains multiple copies of the whole and all of its parts, including itself. That's very holographic, but probably not at all what any sane person wants since JavaScript's GC is refcounted.

If we're dealing with complicated nodes full of canvases and whatnot, you can easily build a staircase to 300MB of RAM consumption or more in just a few minutes of application use if you're adding and removing things on an ongoing basis.


Upon reading that comment, four engineers proceeded to slap themselves in unison. It's an honest mistake, but refusing to fix it is honestly, offensively stupid.

The unfortunate reality is that the more you use Isotope, the more shit you leave floating around in browser memory. The more shit you leave floating around in browser memory, the slower your app runs. You'll probably crash the whole goddamned browser, too, but hey, why would a rockstar ninja Twitter employee need to take that seriously? Computers have all the memory in the world these days and there just aren't enough hours in the day to blog about golden ratios and solve problems that don't involve helping deviant miscreants share seven second iPhone videos of their misshapen genitalia with the world; fuck responsible coding and gimme my $50. P.S. What's a leak?

The Solution

The fix is simple: STOP ASSIGNING RAW JQUERY SELECTOR RESULTS TO EXPANDO PROPERTIES ON THE THINGS THEY WERE SELECTING! Really. That's all it takes. It's not even a heavily guarded industry secret or anything. Here's a really long, really thorough paper about it from IBM:

After plugging this horrible hole, things look much better. When GC runs, all the abandoned content is reaped exactly as it should be:


An admittedly better solution would be to just stop using Isotope, but we all have to ship at the end of the day, and the 20+ hours I wasted tracking this idiocy back to its source was ultimately less expensive than replacing the functionality I needed with something more specialized.

Linters can only do so much, kids.

Original work Copyright (c) 2011-2012 David DeSandro / Metafizzy LLC and all the other people he copied and pasted from.

And here's my license which entitles me to use all of this code in any commercial project I like:

Dec 7, 2012 10:56:20 PST | Receipt No: 2542-5500-0733-6758

Hello Nathan Duran,

You sent a payment of $25.00 USD to Metafizzy.
This charge will appear on your credit card statement as payment to PAYPAL *METAFIZZY.


Merchant information:

Instructions to merchant:
None provided

Payment details

Description:  Isotope Commercial License,  Item #: 13620, 
Unit price: $25.00 USD
Quantity: 1
Amount: $25.00 USD

Total: $25.00 USD

Receipt No: 2542-5500-0733-6758
Please keep this receipt number for future reference. You'll need it if you contact customer service at Metafizzy or PayPal.