Do you really need JQuery?

I’m in your *.js removing your $’s
— me.

Last Monday I started my new job at Red Badger. We're working on a project for the BBC website, helping build interactive learning experiences. We've been given some code created by another agency, and our task is to make it mobile first  and production ready.

Let's leave aside the slight oxymoron of making things "mobile first" after they've already been built - I have work to do - and concentrate on the idea of production ready . What does that mean? Depending on who you work with, this can mean a myriad of things, but usually, it might include any of the following:

  • Remove everything that isn't needed. That includes libraries, imports and other components that aren't strictly necessary
  • Add a building system. This could be Grunt, a Makefile, etc. The idea is to add a few tasks to build, concatenate, uglify and mangle your code, to make it as slender as possible
  • Make the CSS easier to work with - usually by converting it onto something like LESS or SASS, whatever you like. Ideally you want to extract variables and paths out into a file so that anyone that comes after you can customise things easily.

When we started the project, we were given three interaction patterns. The combined weight of all the JS code was around 2 mb. Obviously that wasn't production ready or mobile first.  So we started putting the elements through a crash atkins. The first thing you wanna do is get rid of all the jQuery.

I hear all of you rising up from your Eames chairs in disagreement and, above the clamour, one of you, with shaky voice, asks: "But how do you ensure your code deals with the implementation inconsistencies between browsers?" . Well i'm afraid it isn't 2007 anymore, and for the kind of things we usually do with jQuery, i can assure you plain old vanilla JS is more than enough.

Selecting stuff

Enter the querySelector. I'm sure this isn't news to you, but it was news for me. I was used to either use jQuery, or using document.getElementById. Well, with querySelector and querySelectorAll, you can do essentially everything you can do with jQuery selectors:

var oneLabel = document.querySelector('.label');
var allLinks = document.querySelectorAll('div.link');

A call to querySelector will return the first element that matches the CSS selector you give it. A call to querySelectorAll will return all of the elements that match the selector as a NodeList. These functions are supported on IE 8 and above, so unless you're writing websites for banks (you should stop doing that now) you should be fine.

Changing CSS Properties

One of the other things most of people use jQuery for is to change CSS properties on an object programatically, using the .css() function. This function is super handy, you can just pass it an object with the css props as keys and their value as values. Boom! your selected object's properties have changed.

However, the DOM api has very simple way of doing the same thing. Element has a style object that basically contains all the elements style properties, which you can change by simple assingment.

// This jQuery
$('div.label').css({
'font-size' : '12px',
'color' : '#fabfab'
});

// can be rewritten as:
var label = document.querySelector('div.label');
label.style.fontSize = '12px';
label.style.color = '#fabfab';

Ok, you have to type a bit more, but to be honest, you're probably paid a lot of money to type, and if you use a good text editor, you probably can just tab your way through everything.

If you prefer the .css() notation, why not write your own function?

function applyCss(element, cssProps) {
  for (var prop in cssProps) {
      element.style[prop] = cssProps[prop]
  }
}

I haven't tested that, but i don't see why it wouldn't work. Please bear in mind that the style object is not a good place to learn about all of the css properties of an element. You should use window.getComputedStyle for that. Be careful, as this function is only available on IE 9 onwards.

[Edit] also, be careful if you're changing slashed properties such as background-image. While Chrome will understand what you mean, Firefox wants you to set them as camelCased properties. Thus, background-image becomes backgroundImage, font-size becomes fontSize etc. ;_;

Adding, Removing and checking for CSS Classes.

You can change CSS with javascript, but you really shouldn't. It's hard to maintain and well, if someone wants to make a change on the color you roll-over to, it's going to require you digging into your code. That's why you should be applying classes instead.

It's common practice to add an active or selected class to elements as you interact with them. This is why jQuery's .addClass() and .removeClass() are some of the most used functions in jQuery.

Doing the same thing in javascript used to be very tedious. One would access the .className property of an Element and then do string operations on it to add/remove/check for classes. This was tedious and error prone, so most devs would favour jQuery.

Enter classList. It provides three functions .add, .remove and .contains. They do what they say on the tin:

//Adding a Class in jQuery
$('div.label').addClass('warning');

//Adding a class without jQuery;
document.querySelector('div.label').classList.add('warning');

//Removing a class is the same:
document.querySelector('div.label').classList.remove('warning');

It is a bit more involved if you want to work on a bunch of elements at the same time, but it's no big feat:

var labels = document.querySelectorAll('div.label');
for(var i = 0; i < labels.length; i++) {
  labels[i].classList.remove('warning');
}

If you absolutely need to target old browsers that don't support classList, there's a shim you can use to add this functionality. By old I mean IE8.

Events

Finally, let's have a look at events. You usually see people write something like this to add functionality to a bunch of labels.

$('div.label').on('mouseover', function(event) {
  $(this).addClass('active');
});

On normal Javascript, we could write that like this:

var labels = document.querySelectorAll('div.label');
var listener = function(event) {
  event.currentTarget.classList.add('active'); 
};

for(var i = 0; i < labels.length; i++) {
  labels[i].addEventListener('mouseover', listener);
}

The reason why I kept a reference to the listener function is so that later, we can remove the listeners if we needed to (i.e. if you're templating)

labels[i].removeEventListener('mouseover', listener);

Some people love to pass data with their events in jQuery, i.e.:

$('.somediv').on('click', [this, 'clicking'], handler);

90% of the use cases for this are "hey I can't figure out how to pass a this context to my handler, so i'll just pass it as jQuery's data object and hey presto. Well this kinda sucks and it's totally unnecesary. You can do twothings:

1. Make a HandlerBuilder

Simply, build a function that takes your context and returns a contexted handler:

function contextedHandler(context) {
  return function(event) {
    context.doSomething()
  };
};

you can then do:

document.querySelector('.someDiv').addEventListener('click', contextedHandler(this));

boom, your function is contexted, you have access to your beloved this.

2. Bind your handler's context

The other option is to use Function.prototype.bind(), which allows you to change the this context of any function, by just calling myFunction.bind(this). Your handler could then look like this:

document.querySelector('.someDiv').addEventListener('click', handler.bind(this));

However, bind was introduced in ECMAScript 5, so you'll probably have to add a mixin for people who still like to browse with dinosaurs.

Again, this approach requires a bit more code. My advice is that you buy a nice keyboard. Or, you could design your own, make money from it and probably not have to code anymore. Maybe that is what you want, huh?

Conclusion

Ok, in all seriousness, I think the browser inconsistencies that jQuery was born to iron out have started to fade away, and with them, jQuery's relevance is starting to wane slightly. That doesn't mean you should run out and burn all our jQuery code. It just means that if you're writing things for modern browsers, it's not such a stretch to just use plain Javascript.

Go ahead and give these docs a big read. You'll be a way better programmer.