Skip to content

Instantly share code, notes, and snippets.

@dminkovsky
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dminkovsky/fca08ecdb4ce394aa7e1 to your computer and use it in GitHub Desktop.
Save dminkovsky/fca08ecdb4ce394aa7e1 to your computer and use it in GitHub Desktop.

tl;dr: The problems inherent in jQuery dependency should not be resolved by not depending on jQuery or some other $-providing library. Rather, library authors should write library code that expects a $ will be injected that meets the library's needs.

The problem:

In the front-end JS world, there is a movement to be dependency-free, and specifically to be jQuery dependency free. This problem seems to be the result of a pre-package management mindset. But those days are long gone, or, they should be.

The result of this mindset, however, tends to not be pretty: library authors write their own, often minimally tested, DOM manipulation and utility belt routines. If you come to depend on multiple or many such libraries, your code, via their code, then comes to contain dozens of diverse implementations of similarly or identically-purposed functions.

Some examples:

  • Sortable.js. A wonderful library that's very easily integrated with DOM elements managed by Backbone Views (aka, does not manipulate the DOM itself very much). Code that would be unneccesary if a $ was injected:

https://github.com/RubaXa/Sortable/blob/master/Sortable.js#L465-L506

This library could probably be 30-40% shorter if it depended on some $, and although it is already quite simple and easy to follow, would be that much better with such a dependency.

  • Medium.js. A cool library, though not very useful in terms of Backbone integration. Made less cool by stretches of code like this:

https://github.com/jakiestfu/Medium.js/blob/master/medium.js#L138-L184 (and the code around it too).

This library would be much improved by depending on some $ and some utility belt. Depending on this library means you inherit all those totally unnecessary event binding routes, and all the other similar, supporting code in this library.

  • List.js. Also an interesting library, made not useful by 2 factors:

(1) It attempts to solve the above-state problem by using Component:

https://github.com/javve/list.js/blob/master/index.js#L9-L11

This attempt fails, however, because it's mostly incomplete. The library still has tons of hand-crafted, untested DOM manipulation, utility-belt- type code.

(2) Component is a total drag to use with NPM/Browserify, thus making this library pretty much worthless for anyone using NPM/Browserify.

The solution:

Library code should specify the $ and _ it expects, and a matching $ and _ should be injected into the library.

Thus, instead of writing libraries like:

var $ = require('$')
var isArray = require('lodash.isarray);

// library code

Or, not depending on a $ at all (which is even worse), instead write library code like:

// someLibrary.js
module.exports = function(_, $) {
  return factory(_, $);
};

function factory(_, $) {
  var someLibrary = {
    somethingCool: function() {
      $('.lame').removeClass('lame').addClass('awesome');
    },
    alsoCool: function(something) {
      return _.isArray(something) ? 'is array' : 'not an array';
    }
  };
  return someLibrary;
}

Application programmers can then craft a $ and _ for their application:

// myApplication$.js
var $ = require('domToolbelt.$');
$.fn.removeClass = require('domToolbelt.removeClass')
$.fn.addClass = require('domToolbelt.addClass');
module.exports = $;
// myApplication_.js
var _ = {};
_.isArray = require('lodash.isarray');
module.exports = _;

And then inject those into the libraries they use:

// application.js
var $ = require('./myApplication$');
var _ = require('./myApplication_');
var someLibrary = require('someLibrary')(_, $);

Notice that library code should not require() its own $ or _, because the application author should be able to inject their desired implementation into the library. Although using require() for $ or _ in library code makes sense superficially, it prevents applications from being built in truly modular ways, without the use of repetitive implementations in your final bundle. The application author should decide which browser edge cases they want their application to support, and provide the relevant $ and _.

@HenrikJoreteg
Copy link

interesting idea, but seems to make a lot of assumptions about the capabilities of those injected _ and $ no? Those will vary depending on each library. So it pushes the dependency resolution problem up a layer.

@webpro
Copy link

webpro commented Jun 26, 2014

I tend to agree with Henrik. However, as an example, here's what you could do with DOMtastic right now:

var selector = require('domtastic/commonjs/selector'),
    class_ = require('domtastic/commonjs/class');

var $ = selector.$;
$.fn = {};
$.fn.addClass = class_.addClass;
$.fn.removeClass = class_.removeClass;

module.exports = $;

You can also mix in other methods from other libs, but they must very specifically work "the jQuery way" (collection of DOM elements, return this for chaining).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment