Strongly coupled with the past

This post is not meant to judge anyone and it surely doesn’t want to be a replacement for “you might not need jquery“ website.

However I’ve found myself (again) in the situation where I’d like to pick a well tested, fast and reliable CSS abstraction, but I don’t want to choose a whole framework or library JavaScript overhead together with such CSS solution.

The Bootstrap 4 alpha, the latest/greatest release of all Bootstrap versions, a framework I respect and like since about ever, turned my excitement down as soon as I’ve realized it throws errors if you don’t include jQuery upfront.

Don’t get me wrong, I’m a strong supporter of jQuery … but let’s say I support it in a different way. If you’ve read the Secrets of the JavaScript Ninja book, you’ll find my name as one of those external contributors that suggested initial cross-platform fixes and hacks for some weird case, quite frequent situation you would find at that time … but …

TL;DR I hope this is no news for you, jQuery is a library born in a time where pretty much everything was failing cross-platform, and it has been pushing Web boundaries so much that even W3C started adopting some of its methods: kudos to that!

The same way the old Prototype library influenced “modern“ ECMAScript (JS) standards, jQuery has been changing, and improving, over all these years the web: respect!

However

Stuck in a range of library versions

When I’ve quickly checked Bootstrap 4 alpha, it kinda scared me to read its first lines of code:

// [MY COMMENT]
// strongly coupled with a global scope jQuery
if (typeof jQuery === 'undefined') {
  throw new Error('Bootstrap\'s JavaScript requires jQuery')
}

// [MY COMMENT]
// badly coupled with a very specific,
// and not future-proof, range of versions
+function ($) {
  var version = $.fn.jquery.split(' ')[0].split('.')
  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] >= 3)) {
    throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v3.0.0')
  }
}(jQuery);

Let me quickly recap: the alpha version of the potential new foundation of your next project, is already trapped behind a “but less than v3.0.0“ check, meaning your business will, by explicit software contract, need changes in the near future.

You can think this is somehow good news, if you’re looking for some maintenance “job security“ thing, but I honestly would think already: “next foundation candidate, please!

I’ve written websites in year 2000 that are still up and running, and became faster and faster thanks to polyfills that, once implemented natively, became surely obsolete, but never obtrusive.

These sites are still used, and while I’m sure I might consider to re-style them one day, I go to sleep every night knowing that these sites will always work and be compatible with tomorrow browsers.

This is why it is a big decision maker, at least to me, knowing that my next big project won’t fade out of support due some redundant dependency, buzzed daily framework, or die-hard library … and bonus points if my next project will boost up automatically as soon as my polyfilled new standards will be widely adopted!

The Web is a better environment these days

Still using Bootstrap as example, but I can assure you this is not strictly a Bootstrap related issue, I’ve noticed there are few jQuery calls that could be completely removed … and for good, making few bits of the project less coupled with its monolitic dependency.

Following just few sporadic gotchas I’ve noticed in there:

// jQuery
$(element).closest(className);
// DOM https://dom.spec.whatwg.org/#dom-element-closest
element.closest(className);

// jQuery
$(element).trigger($.Event(type));
// DOM https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
element.dispatch(new Event(type));

// jQuery
$(element).find(selector)[0];
// DOM https://www.w3.org/TR/2015/WD-dom-20150428/#elements
element.query(selector);

// jQuery
$('ul li').addClass(function(index) {
  return 'item-' + index;
});
// DOM https://www.w3.org/TR/2015/WD-dom-20150428/#elements
document.queryAll('ul li').forEach(function (li, index) {
  li.classList.add('item-' + index);
});

// jQuery
$(element).hasClass(className);
// DOM https://dom.spec.whatwg.org/#domtokenlist
element.classList.contains(className);

// jQuery
$(element).toggleClass(className);
// DOM
element.classList.toggle(className);

// jQuery
$(element).addClass(class1).addClass(class2);
// DOM
element.classList.add(class1, class2);

// jQuery
$(element).one(type, handler);
// DOM https://dom.spec.whatwg.org/#interface-eventtarget
element.addEventListener(type, handler, {once: true});

OK, OK … I got it, you also want the chained $(element).on(type, handler) one, something kinda de-facto standard, so … we can all agree on the following upgrade:

(function (w) {

  function on() {
    this.addEventListener.apply(this, arguments);
    return this;
  }

  (
    w.EventTarget ||
    w.Element ||
    w.Node
  ).prototype.on = on;

  if (!w.EventTarget) {
    w.on = on;
    w.document.on = on;
    w.XMLHttpRequest.prototype.on = on;
  }

}(window));

which will give us the ability to use .on(...) as well:

// jQuery
$(element).on(type, handler);
// DOM + 20 lines of extra code
element.on(type, handler);

// jQuery
$(element).one(type, handler);
// DOM + 20 lines of extra code
element.on(type, handler, {once:true});

The list of things that got easier to write, and with native support and performance, doesn’t stop here: ChildNodes methods to before, after, replaceWith, and remove, ParentNode methods to prepend or append, .matches(CSSSelector), Animation interface and much more … are you convinced yet the Web heard developers and it’s moving faster than it used to move and for good?

Yet something is missing …

If you’ve read this much you might still wonder “then why are they still using jQuery?“ and that’s a very valid question.

I’ve asked the same myself and the most plausible answer is that jQuery is very good at plugins integration, and Bootstrap has few of them.

This is probably the elephant in the room nobody wants to see, but having a common way to define functionality for every DOM element or list of elements in a document, is the biggest missing part of the Front End ecosystem.

15 years ago developers tried to pollute the Object.prototype in order to upgrade common functionalities per each DOM node.

10 years ago jQuery became this popular because it was the main, globally shared, entry point to enrich every single bloody search or single node with ease.

The ECMAScript 2015 Standard finally introduced classes able to subclass natives, like class List extends Array {}.

Regardless, there’s still this wall between plain JavaScript and its DOM counterpart, and while prototypes are exposed and able to be upgraded, like I’ve done in the previous example adding .on(...) method to all nodes, nobody dares to actually pollute them.

Why? Because we’ve been teaching developers that is bad, and yet everybody can create today a new jQuery plugin without feeling even bad. Guess what, that’s the exact same of polluting globals, if everyone believes jQuery is there by default and you can update its prototype!

… even Chrome console …

I remember a Lea Verou‘s talk long time ago stating, at some point:

Have you ever tried to open an about:blank page and realize that’s the most clean environment you can think of in a browser?

At some point in time, somebody at Google decided that polluting the console, also known as developer tools, was a good idea.

There are people believing that $ is a globally available reference, and this is the worst seppuku move a browser aiming for better standards could do: put in everyone mind the $ is a well known and better way to deal with the document … can I add a little WTF in there?

Open a new tab, url to about:blank and just type $ in the console … you know what that is?

A very obtrusive environment that won’t let your code decide if typeof $ is defined or not in the global scope: slow clap for dev tools here.

How to create my own jQuery?

As silly as this question sounds, there are reasons I’ve mentioned on why jQuery is popular, and the biggest point is its plugin-ability.

If you’d like to have a similar base for your own jQuery-sh library, you can give this snippet a try:

var augmentWith = require('augment-with');
var augmentWithArray = require('augment-with-array');

// internal function, facade through the $ one
var Query = augmentWith(
  augmentWithArray,
  function Query(array) {
    this.push.apply(this, array);
  }
);

// the exported function
function $(selector, parent) {
  return typeof selector === 'string' ?
    new Query((parent || document).querySelectorAll(selector)) :
    (selector instanceof Query ?
      selector :
      new Query(selector ?
        Array.prototype.concat(selector) : []));
}

// link the right prototype
$.prototype = Query.prototype;
$.fn = $.prototype;

// what is exposed
module.exports = $;



// basic on/off/dispatch plugin example
(function () {

  function commonLooper(method) {
    return function () {
      for (var el, i = 0; i < this.length; i++) {
        el = this[i];
        el[method].apply(el, arguments);
      }
      return this;
    };
  }

  $.fn.on = commonLooper('addEventListener');
  $.fn.off = commonLooper('removeEventListener');
  $.fn.dispatch = commonLooper('dispatch');

}());

// simple live test
$(document).on('click', function () {
  console.log('hello world');
});

Yeah, with a couple of scripts that are made out of few lines of cross-browser JS, you can implement functionalities you like the most in few other lines … how cool is that?

But not every browser is up-to-date

I know, which is one of the reasons I’m the author of dom4 polyfill, able to bring in, down to IE8 if needed, or any other Mobile browser, pretty much everything shown in this post.

The missing polyfill in DOM4 would be the Web Animation one, a good one that fits in about 13KB once gzipped.

As Summary

There are always pros and cons about using an abstraction, but there are also few things, as developer, you should probably think about … if you recognize yourself in this list, please take the hint with no offence:

Once again, this is not exactly a battle against jQuery, rather a hint for modern Web developers, please don’t take it personal and feel free to ask anything you’d like to clarify: thank you!

Andrea Giammarchi

Fullstack Web Developer, Senior Software Engineer, Team Leader, Architect, Node.js, JavaScript, HTML5, IoT, Trainer, Publisher, Technical Editor