Replacing jQuery (110kb) With UmbrellaJS (8kb)
bennadel.com
bennadel.com
I'm glad others are finally coming to this conclusion as well. I've been saying this for over a decade: jQuery success was not only cross browser compatibility. There were plenty of competing libraries at the time that offered it. jQuery won because of its powerful, composable and succinct API. Nothing has come close to it still for DOM manipulation.
And also: direct DOM manipulation is not evil, it just needs discipline, but so does programing in general.
> Please don't post shallow dismissals, especially of other people's work. A good critical comment teaches us something.
If you haven't keeping up with all the new script-tag parameters, I suggest looking at this: https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8af...
Choosing type=module is usually better choice than classic script tags like in the article. Module tags fetches (if any modules is used) asynchronously, and executes after the parser, however it won't work with older browsers.
const $ = sel => [...document.querySelectorAll(sel)];
But then you get to it and are adding a lot of classes, so you might add a couple of helper methods: const addClass = (col, cls) => col.forEach(el => el.classList.add(cls));
const removeClass = (col, cls) => col.forEach(el => el.classList.remove(cls));
addClass($('ul li'), 'list-item');
Oh and you might need to inject content before/after, that's a bit trickier but thanks to the classic You Might Not Need jQuery[1] we know how to do it: const insertBefore = (col, html) => col.forEach(el => el.insertAdjacentElement('beforebegin', html));
const insertAfter = (col, html) => col.forEach(el => el.insertAdjacentElement('afterend', html));
insertAfter($('ul li:last-child'), 'New todo item');
Keep going a bit like that, until you realize you are basically reinventing jQuery but pretty badly, undocumented, and untested.Add a couple of very nice-to-haves, like chaining (instead of nesting in these examples above) and prototypes and that's what Umbrella JS is, very thin methods to manipulate the DOM and handle events. In fact, compare our "addClass" implementation in this comment to Umbrella's addClass[2], it's almost the same size but hundred times more flexible:
// Add class(es) to the matched nodes
u.prototype.addClass = function () {
return this.eacharg(arguments, function (el, name) {
el.classList.add(name);
});
};
[1] https://youmightnotneedjquery.com/[2] https://github.com/franciscop/umbrella/blob/master/src/plugi...
export function each(qs, cb) {
if (typeof qs === "string") {
qs = document.querySelectorAll(qs);
}
if (!qs) {
return;
}
if (qs.length === undefined) {
qs = [qs];
}
for (var i = 0; i < qs.length; i++) {
cb(qs[i], i);
}
}
export function on(ev, qs, cb) {
let cancelFns = [];
each(qs, (el) => {
el.addEventListener(ev, cb);
cancelFns.push(() => {
el.removeEventListener(ev, cb);
});
});
return () => {
cancelFns.forEach((fn) => {
fn();
});
};
}script type="module" src="module.js"
script nomodule src="nomodule.js"
Also it provide some kind of JS hijack. All the code with in the module can be tainted by a random .js included elsewhere in the page. However code in module can still access the window object to have access to global and the dom.
That said, in addition to forcing me to restore a lot of fun animations that I had previously broken while porting the site from AngularJS to Angular, it also just ended up making the code a lot better. A lot of the times when we were doing something with jQuery, the same thing could have been done more cleanly using [ngClass] or whatever. So taking the three or four days to remove jQuery actually made the business logic of the site much easier to understand. Performance benefits aside, I'd definitely recommend it for this reason alone.
Does it though? Even years after moving on from jQuery it’s always very clear when reading old code that all selectors will be matched. There’s less cognitive load knowing you’re querying all matching items by default than mentally parsing the difference between querySelector and querySelectorAll, and also having to spread the result because NodeList doesn’t support array functions.
Zepto supports some methods that Cash doesn't, but you probably shouldn't use them to begin with, like $.ajax, $.isArray, $.fn.animate etc. In 2022 either better built-in solutions exist or better specialized tiny libraries exist.
Everything that is supported by both Zepto and Cash should either work identically or Cash's implementation should be closer to jQuery's. Just to mention one thing in this regard you can run jQuery's test suite with Cash, and Cash's test suite with jQuery, easily [0]. I've done so and looked at every single failed test manually a few times, I doubt nearly the same level of attention went into Zepto. Just to mention one difference: Cash supports jQuery-style event namespacing, Zepto just doesn't support this, it is implementing something that looks similar but that works differently in pretty important aspects, that's worse than not supporting obsolete methods IMO, but I guess it depends on your use case for a library like this.
[0]: https://github.com/fabiospampinato/cash/blob/272132a6dc1d885...
Also, and it seems to be something the author of the article likes a lot, Umbrella JS is closer to Javascript than the libraries you mention (& jQuery). Sure that means you need a bit of editing your code (which is made easy by being able to load both jQuery and Umbrella at the same time), but in the end you have a lot better result. Plugins are just methods extending the prototype, loop methods have similar arguments as Array's methods, which in turn makes it easier than ever to use arrow functions, etc.
But IMHO the killer feature of Umbrella JS is that it's quite well documented, every method has at least an example but usually few, where e.g. cash doesn't have any example interleaved.
Basically this would be my recommendation/conclusion:
- If you have thousands of lines of Javascript/jQuery, I'd recommend Cash/Zepto instead.
- If you just use jQuery for few methods/interaction, 100% go with Umbrella JS.
I'm ambivalent regarding your documentation point, on one end jQuery is way more popular and one can largely just read jQuery's documentation for the methods that Cash implements too. On the other end that doesn't quite work when Cash's implementation works a bit differently than jQuery's, and just having a consistent, good, documentation is better in that case, and probably in general too.
I don't personally care much which libraries people use for their needs, personally I'm trying to incrementally move away to Cash and other jQuery-like direct DOM manipulation libraries entirely, but it takes time.
Articles can absolutely just be seeds for discussion, and I think the theme of replacing jQuery is quite relevant across the industry as motivators like "support IE" are increasingly falling away.
$ = (s, p = document) => p.querySelector(s)
$$ = (s, p = document) => p.querySelectorAll(s)
If I’m feeling arrayish I’ll wrap the latter in [...qSA] so I can use .map and the rest of them, otherwise for/of works just as well.Anyone using $.animate() and $.ajax() this year should probably stop.
For example I can do the following with jQuery:
$(document).on ( 'click.ns', '.button', callback );
How should one do the same with vanilla JS? You'd end up rewriting a (probably buggy) reimplementation of jQuery's $.fn.on method in the end.I would not start a new project with jQuery nowadays just for that stuff.
The fact that $(s).remove() never throws for example wouldn’t be ok for me anymore, so I’m looking at more cons than pros.
> How should one do the same with vanilla JS?
And you said:
> With the code here
Here's [0] an article about the usefulness of namespaces, it has use cases beyond custom events, which have been implemented a real long time ago.
So its not really 'replacing' JQuery is it?
(no knock meant to @franciscop who didn't write the article or the title)
So I understand the desire to write your own libraries and use them. But isn't the author just replacing one third-party library with another? How is that really any better?
Aside from the size which really seems to be a bizarre thing to be commenting on. I've seen png files used for buttons that were larger than a compressed JQuery library
> That's a massive savings on size. Of course, there are some sacrifices taken in that reduction.
My experience is that, even with a slow connection and a slow smartphone, removing 30kB of JS would have very little impact. The exact gain is smaller: the size difference of the gzipped-6 stable releases is 27kB.
Phoenix Elixir Tailwind Alpine.js LiveView
In fact, LiveView alone probably replaced like 90% of what jQuery was ever needed for. The remaining 10% is done with Alpine.
Luckily, we now have frameworks that help us avoid most of the DOM manipulation stuff.