How to Stop Using jQuery
📣 Sponsor
jQuery is a widely used tool across the web, and great for accomplishing things quickly in frontend Javascript development. When I started coding, jQuery was an amazing tool for helping me accomplish my goals fast. It also helped that Javascript was still a developing language - I remember having to support IE7, which didn't have any support for querySelectorAll
for example.
Up until relatively recently, jQuery's functionality was still really useful. Over the years, however, vanilla JS has slowly caught up, old browsers have died out, and chances are, jQuery is now working against some of the objectives of your website or app, speed being the most obvious one.
Let's take a look at the major things jQuery has been good for in the past, and how you can slowly wean yourself off them (along with a speed comparison).
Selectors
jQuery's major strength was always DOM selection and traversal. There is a slight problem with jQuery selection, however, and that is it hides the true amount of operations you are doing in your code. This can lead to bloated and slow websites, as you utilise what seems like a simple syntax, but underneath, is running many loops.
$('.button').addClass('start');
From the above code, it seems like not much is happening, but jQuery is going through every .button
DOM element individually and adding a class. If you suddenly have hundreds of .button
elements, then under the covers jQuery is doing a lot of processing. This inevitably slows down your application or website.
It gets even worse if you decide to chain selectors, which can result in many hundreds of operations for what seems like a straight forward DOM traversal. Let's take a look at the vanilla JS alternative:
let selector = document.querySelectorAll('.button');
// Modern browsers
selector.forEach(function(item) {
item.classList.add('start');
});
// If you need older versions of IE
Array.prototype.slice.call(selector).forEach(function(item) {
item.classList.add('start');
});
querySelector
gives you all the flexibility of CSS selectors, and the forEach
statement makes it clear how many operations you are doing. Only want to affect the first element? Use querySelector
. Want to affect an ID? Use getElementById
. How much faster is vanilla JS? Take a look:
Attribute Selection
Perhaps the most similar of them all, attribute selection looks basically the same for jQuery and vanilla JS:
$('.button').attr('data-width');
document.querySelector('.button').getAttribute('data-width');
But the speed difference tells a different story:
🥈 Fade In/out
Fade in and out was, at one time, a major thing tying me to jQuery, but it's so simple to replace - just add a class where the class transitions to an opacity of 0, or 1.
document.querySelector('.button').classList.add('fade-out');
.button {
transition: opacity 0.2s ease-out;
opacity: 1;
}
.fade-out {
opacity: 0;
}
And how much faster is that than fadeOut()
? Although vanilla JS is more than 300 times faster, fadeOut()
still only clinches silver in our jQuery speed test.
Parent and Child Matching
Another crutch, and another reason to stick with jQuery. Matching up and down the DOM tree is so much easier with a simple piece of jQuery like this, which matches any parent element of .button
:
$('.button').parents('#container');
But does it have to be this way? For two simple functions like parents()
and children()
, we can surely write our own. Here are some functions I put together that do the same thing as this function, in vanilla JS. Here they are for children and parents:
// parent: matches the first matching parent in the DOM Tree
// @el[string]: el you want to find a parent of
// @match[string]: css selector for the matching element
// @last[boolean]: if true, only the last element will be returned. If false, all parents to that element are returned
const parent = function(el, match, last) {
var result = [];
for (var p = el && el.parentElement; p; p = p.parentElement) {
result.push(p);
if(p.matches(match)) {
break;
}
}
if(last == 1) {
return result[result.length - 1];
} else {
return result;
}
};
// child: matches the first matching child in the DOM Tree
// @el[string]: el you want to find a children of
// @match[string]: css selector for the matching element
// @last[<]boolean]: if true, only the last element will be returned. If false, all children to that element are returned
const child = function(el, match, last) {
var result = [];
for (var p = 0; p < [...el.children].length; ++p) {
result.push([...el.children][p]);
if([...el.children][p].matches(match)) {
break;
}
}
if(last == 1) {
return result[result.length - 1];
} else {
return result;
}
};
And how much faster is that? It depends on your DOM size, but on a DOM tree with five levels, here are the results. Again, vanilla JS clinches the speed victory:
For Each
jQuery comes with its own forEach, which can be used in tandem with a jQuery selector, or an object or array. Here's what both of them look like:
// Vanilla JS ForEach
document.querySelectorAll('.button').forEach(function(item) {
item.classList.add('start');
});
// jQuery
$.each($('.button'), function(k, v) {
$(v).addClass('start');
});
Obviously, the jQuery forEach is a little more intuitive. It will automatically parse objects, for example, whereas in vanilla JS you have to use Object.keys
.
However, as you may have guessed, the jQuery version is more inefficient than vanilla JS. In fact, jQuery runs about twice as slow:
Ajax/POST/GET
Ajax requests kept me coming back to jQuery for much longer than I really needed to. However, it's not too hard to construct your own Ajax requests with vanilla JS. For example, imagine you want to POST something. In jQuery you write:
$.ajax({
type: 'POST',
data: data,
url: '/some/url/to/data',
success: function(e) {}
});
In vanilla JS, you have to write this:
fetch(url, {
type: 'POST',
headers: {}, // If you want headers
body: data
}).then((data) => {
// do something with the data
// Same as success: function() in jQuery
});
👑 Creating New Elements
Creating new elements in vanilla JS can seem a little bit harder, but the benefits can be huge in terms of processing speed. I would also argue creating with vanilla JS makes your code a lot clearer. Here is how creation of a new element works in vanilla JS and jQuery:
// Vanilla JS
let el = document.createElement('div');
el.classList.add('button');
el.innerHTML = '<div><div><div></div></div></div>';
document.body.appendChild(el);
// jQuery
$('body').append('<div class="button"><div><div><div></div></div></div></div>');
As you might have guessed, this takes the crown for our slowest jQuery process, with vanilla JS coming in at 800x faster.
Conclusion
When I started this, I was expecting minor speed improvements with vanilla JS - but the differences here can have a big effect on the overall speed of your website or app. Multiply these small changes by 100s of jQuery calls, and suddenly your website becomes slow and more unresponsive. Not only that, but jQuery isn't exactly tiny either, coming in at roughly ~90kb.
Changing an ongoing project from jQuery to vanilla JS can be quite a demanding undertaking, but next time you start something new, consider doing it without jQuery. It might make all the difference.
More Tips and Tricks for Javascript
- An Introduction to Javascript Objects
- How the Javascript History API Works
- Creating and Generating UUIDs with Javascript
- What are the three dots (...) or spread operator in Javascript?
- How to select HTML elements in Javascript
- Javascript Logical Statements and Loops
- Making a Morphing 3D Sphere in Javascript with Three.js
- How to install nvm, the Node Version Manager
- How Generator Functions work in Javascript
- Javascript Immediately invoked function expressions (IIFE)