By Patrick Kunka
MixItUp 3 launches today.
I started the process of "excising" jQuery from MixItUp exactly 2 years ago. With some much larger-scale projects on the go over that time, I often wondered if I'd ever manage to ship it. But several rewrites and a long period of beta testing later, I'm pleased to announce that the general release of MixItUp 3 is finally here. In this post, I'd like to present some of the new features that I'm most excited about, and discuss the design decisions that led to this point.
As we've all seen, the past 2 years have brought huge changes across front-end tooling, frameworks, and general approach. I'm amazed and humbled that MixItUp 2 (which will be 3 years old this March and largely unchanged since its initial release) remains hugely popular today - despite its dependence on jQuery, global variables, and at its core, a now somewhat-dated approach based on querying the DOM (more on that later).
The front-end innovations of the past 2 years have affected the design direction of MixItUp in many different ways. The coming-of-age of promises, modules, DOM abstraction and ES6 during that time all resulted in some major design quandaries that had to be overcome to get to this point. But now we're here, let's take a look at what's new!
The motivation behind dropping jQuery was two-fold. As long as MixItUp remained a jQuery plugin, each year, more and more developers would be put off and unable to consider it for their projects. On top of that, I haven't personally used jQuery since 2014, so I had no interest in maintaining a product I wouldn't use myself. But most of all, I wanted to go deep into native DOM APIs in the hope of overcoming the many performance bottlenecks in MixItUp 2 caused by a combination of jQuery and poor architectural decisions. The biggest performance problems in MixItUp 2 were all related to working with large sets of DOM elements. While pure client-side filtering and sorting will always have its limits, the practical limit of around 100 elements imposed by MixItUp 2 left a lot to be desired and was something I felt could be dramatically improved.
Early MixItUp 3 benchmarks were extremely exciting, and I've worked hard since then to ensure that those performance gains were not lost as overhead was added back on top of the initial prototype.
Thankfully, benchmarks remain impressive and as you can see from the following chart, show drastic improvements over MixItUp 2.
Random sort –
mixer.sort('random') – is a perfect operation for benchmarking as it involves a bit of everything: sorting algorithms, target indexing, and a considerable amount of DOM manipulation as all targets must be removed, re-ordered and inserted back into the DOM on each operation.
The chart above shows the results of a test measuring the run time of 100 random sort operations (with no animation), against increasing numbers of elements. While we can see that both versions are running in some form of O(n) time, MixItUp 3 offers a substantially smaller coefficient meaning that working with large datasets (i.e. thousands of elements) now becomes a real possibility.
Factory Function and Mixers
Without jQuery's plugin syntax, the first problem when designing MixItUp 3's API was figuring out what instantiation should look like. MixItUp 2 followed a quasi-object-oriented approach, with instantiation abstracted away by the jQuery plugin syntax. I've always admired jQuery's API and the way in which it keeps the barrier to entry as low as possible by hiding anything potentially confusing to JS newcomers.
With that in mind, I thought a factory function would be a good way forward, abstracting the process of "newing-up" MixItUp instances while offering numerous other benefits such as caching.
So not too far from jQuery. But as a factory function, it should return something - in this case a reference to the newly instantiated "mixer".
By not forcing users to manually "new up" mixers, we can do all sorts of error checking and optimization within the factory function, as well as return whatever we like from it. Additionally - by separating the factory API from a mixer's public interface, we then open up the mixer-level API to return whatever we want too - which brings us on to the next feature.
.multimix() now return promises, which resolve with a state object.
So that's the high-level API out of the way - let's take a look at some of the new features:
Toggle and MultiMix Controls
While MixItUp 2 provided a
multimix() api method, control buttons were limited to either filtering or sorting exclusively. MixItUp 3 allows both
data-sort attributes to be present on the same control button, essentially creating a multimix button.
Likewise in MixItUp 2, filter buttons were either straight-forward filters, or toggle filters, depending on your configuration. MixItUp 3 allows filter and toggle buttons to exist side by side thanks to the addition of a new
data-toggle attribute which can be added to controls.
.toggleOff() mixer API methods have also been added to ensure that every piece of functionality has an API equivalent.
Insertion and Removal API
While MixItUp 2 had an
.insert() method, its functionality was not particularly robust, nor did it work reliably when combined with filter and sort operations. More critically, MixItUp 2 lacked any sort of remove method. The only way of removing elements gracefully involved filtering them out, deleting them from the DOM manual, and finally hard-refreshing the mixer.
MixItUp 3 tackles all of these shortcomings with a powerful set of insertion and removal methods that integrate fully with all other multimix-compatible operations.
As mentioned earlier, MixItUp hails from the jQuery era when developers were free to interact with and manipulate the DOM with impunity. Nowadays (mostly thanks to React), that kind of approach seems – lets face it – a bit vulgar with the DOM now seen as a final rendered representation of your application's data and nothing more.
Find out more in the Using the Dataset API tutorial.
NPM and Module Support
MixItUp 3 adds full ES2015 (via webpack), CommonJS and RequireJS/AMD module support, while retaining the ability to be loaded as an old-school global from a
Lack of module support was the only thing preventing the publishing of MixItUp to NPM. So with that problem solved, I'm pleased to announce that MixItUp 3 has now been published to NPM.
Due to the particularly long period of beta testing that has gone into MixItUp 3, I was able to identify the most common errors and gotchas that users might experience when working with the library. MixItUp 3 comes bundled with meaningful error messages that are intended to anticipate bugs and instruct rather than just confuse or send you straight to GitHub issues.
Many new configuration options have been added to MixItUp 3 allowing for greater customization of things like animation, filtering behavior and layout. Here's just a few of them:
animation.effectsIn / animation.effectsOut
These two options allow the different animation effects to be specified for targets being filtered in and targets being filtered out.
Similar to the above, the reverseOut option reverses the direction of effects for targets being filtered out. For example,
translateX(-100%) would become
translateX(100%). This is a great way to create a carousel-like effect when filtering between exclusive data sets.
"Nudging" has been part of MixItUp's animation algorithm since v1, whereby 3 different positions or "DOM states" are taken into account when calculating the trajectory of targets to be translated laterally. Sometimes, however, this movement can be overkill when a more subtle animation is needed. The
animation.nudge option provides the ability to filter between exclusive datasets with no lateral movement. Check it out for yourself in our sandbox demo.
This new callback (and corresponding event) allows the handling and interception of control button click events. The ability to call
<a>-based controls was a much-requested feature in MixItUp 2 which is solved by this function exposing the original click event as a parameter.
Another frequent gotcha with MixItUp 2 was unintended interference between multiple MixItUp instances in the same document. The
controls.scope option allows the setting
'local' control scoping to ensure that controls can only interface with their respective mixers.
A particular shortcoming of working with toggle filters in MixItUp 2, was the need to specify all individual active filter selectors as a compound filter on startup rather than simply
'all'. This issue has been fixed in MixItUp 3, and the
controls.toggleDefault option allows for additional specificity around the active filter when all toggles are turned off.
Previously, this would default to a blank filter selector or
'none', but setting controls.toggleDefault to
'all', provides a much more intuitive toggling behavior, where all targets are shown by default. This is also the default toggling behavior going forward.
New in MixItUp 3, is the
classNames configuration sub-object. The various properties within it provide granular control over the classnames that MixItUp adds to elements in the DOM (e.g. active controls), and allows for complete consistency with your project's CSS naming convention (BEM or otherwise).
Many other configuration options have been added beyond those listed above, so be sure to check out the Configuration Object documentation page for the full list.
MixItUp remains highly extensible, and its extension architecture has been redesigned to accommodate module loading via the static method
MixItUp Pagination has also had a complete rewrite with the addition of many much-needed features as well as better compatibility with insertion and removal.
A brand-new extension MixItUp MultiFilter has also been released today offering multi-dimensional filtering between groups of selectors using various combinations of logic. Multi-dimensional filtering was something only available in MixItUp 2 via a couple of rather confusing codepen demos. I know that various users were forced to put that code into production without any documentation or understanding of what was going on – which wasn't ideal at all. I've now taken that functionality, made it highly generic and configurable and moved it into a fully documented extension.
More new extensions are also on the way, including MixItUp Drag & Drop which should be ready some time in Q1 2017.
So that's just a short introduction to some of MixItUp 3's new features, but there's also a lot more I could talk about in terms of the design decisions made over the past 2 years. Without going into too much detail, here's just a few talking points that I feel might warrant an explanation for anyone wishing to take a look at the source code.
A lot of MixitUp's algorithms (particularly those around filtering, sorting and DOM insertion) are already extremely performance sensitive and so the added abstraction of transpilation was something I ideally wanted to avoid. On top of that, I knew that dropping jQuery would inevitably mean a heavier file size, and so it was important for me to maintain a high degree of control over the final structure and contents of the distribution code. MixItUp 3 remains therefore an ES5 project (for now).
This decision goes against the grain of many of today's client-side libraries which continue to migrate their source code to ES6, and my ambition will certainly be to do this in the future, but I will most likely wait for widespread native browser support first and then my hope would be to offer both a light-weight native ES6 version (without IE polyfills) as well as the full transpiled version for legacy support.
Also worth noting is that for now, all of MixItUp 3's documentation examples are written in ES5 with the hope of keeping the barrier to entry as low as possible. Again, once ES6 becomes ubiquitous, these will be re-written.
Many of today's client-side UI libraries are adopting modules internally, which are then simultaneously bundled and transpiled for ES5 distribution using tools like Webpack and Babel. The ability to use bundled modules in the browser at present (whether ES6, CJS, or AMD) does however require a fairly significant overhead whereby the code to parse, require, and manage the plumbing between modules must be bundled along with the software. With the distribution file size a concern, I opted for a more old school approach without such an overhead: namespace encapsulation.
As the size and complexity of MixItUp 3 grew during development, some sort of internal module structure quickly became essential for the sake of code organization. Taking inspiration from the architecture of some of Google's client-side libraries that I've been using over the past two years (Shaka for one), I opted for namespace encapsulation via static members of the factory function, coupled with my own custom build script. This has actually worked very well, and proved particularly useful for debugging and testing whereby private interfaces and classes remain externally available when necessary. Again, my long term ambition would be to move to the ES6 module standard once widespread native browser support arrives, and the overhead can be reduced.
If you're interested in anything above, the source code is as ever, available to check out on GitHub.
Thanks to everyone who's used MixItUp over the past 4 years. Your continued interest in it over that time – even in the face of approaching obsolescence – has encouraged me to keep going with it despite significant resource challenges, and bring it to the level I always hoped it could it be. On top of that, please know that the contribution from those of you who have commercially licensed it for your projects has gone a long way to funding the hundreds of hours that have gone into version 3 over these past 2 years.
I can't wait to see what you build!
Have a question about this article? Leave a comment.
Code can be added to comments via permitted Disqus HTML tags.