I maintain an alternative to jQuery called Cash [0], this looks cool! If you are interested in joining forces OP it'd be cool to have this capability in Cash itself. IMO that'd be the best of both world because this feature is cool and useful, and Cash's methods are most probably closer to jQuery's, better tested and there are more of them available (76 vs 44).
For example, your `on` method [1]:
- Doesn't support event delegation.
- Doesn't support event namespaces.
- Doesn't support receiving an argument mapping events to callbacks like jQuery also can.
- It seems to have subtle bugs, like the way the events string is split makes so that double consecutive spaces in it (which can happen as a result of a typo) will result in listening to the empty string event. Basically: 'foo bar'.split ( ' ' ) => ['foo', '', 'bar'] (there are two spaces between foo and bar).
The `on` method we are using in Cash [2] is a lot more convoluted than that. On one hand it requires more bytes, but on the other the chances of it behaving exactly like jQuery's are much higher. In fact we can also run jQuery's test suite with Cash to spot issues.
Feel free to ping me if you are interested in joining forces.
Sure, Cash is super cool. I wrote this library for my personal use. As I was repeating the same work on multiple projects.
While removing jQuery dependency, the hardest part was finding the jQuery methods in the existing project and writing the alternative vanilla js methods without making much changes in the codebase.
Yes, I agree with you the events part needs to be improved and well documented. (It actually supports namespacing.) I fixed most of the things in https://github.com/sachinchoolur/tiny-events.js and need to make the changes here as well.
My intention was not to build another JavaScript utility library. I just wanted to make my JavaScript libraries jQuery independent.
Kinda accurate, but as soon as you realize that "tool" is basically another name for "library" it makes sense. Like OP replaced code with code at the end of the day, who cares if you call it "library" or "tool" or something else.
Seems pretty cool. The only Cash design decision I’m doubting is $().append not accepting plain text. I get the reason why, but the alternative is ugly. Maybe add an appendText method, or a utility $.textNode?
I’m kind of assuming the underlying philosophy of the project is to make DOM manipulation as easy as in jQuery but smaller by removing seldom needed safeguards and relying on modern browser selectors. I’m okay with the additional footguns but think it should still preserve easy, concise syntax.
That's kind of a mistake of the library IMO, but changing that requires pushing a major release and I'm not sure if it's worth asking everybody to update their code to align that with jQuery at this point.
Vanilla JS refers to Javascript that uses native browser methods instead of relying on a library. This is just replacing jQuery with another library constructed on the fly, but there are already minimal and modern alternatives with the jQuery API like zepto and cash.
Actually, VanillaJS is now a library. It can be downloaded in 0KB, or 25KB gzipped. Not to be confused with “Vanilla JS”. It comes with the famous “Math” library that Google and Strip use, for 0KB additional.
Cash's maintainer here. IMO it could be worth for some use cases to use a replacement for jQuery, for the record Cash is ~75% (~20kb minified and gzipped less) smaller than jQuery Slim, that isn't/shouldn't be a rounding error for many use cases.
These are good points, people are more so taking issue with the title.
I was expecting a tool that just tells me how to do something in Javascript - without any library - that I would be tempted to use just include jQuery for.
Instead I'm presented with a library.
I expected the comment sections to be talking about how AI/ML crowdsourced code aggregators keep messing up, instead I'm reading about a simple additional convenience library that a contractor wrote for themselves, and described wrong.
I first thought that it replaces jQuery with vanilla, but it replaces jQuery functions with other functions? With this point of view using the term vanilla would mean that there is no difference to jQuery, because in the end jQuery is also written in vanilla? Or do I get something wrong here?
This is more like tree-shaking jQuery into just the pieces you need from it, so you aren't loading up the full library when you are only using 20% of it.
While this is technically interesting, I can't help but ask what the benefit of this is? Instead of using a library that's battle-tested and well documented you'll now have to maintain an ad hoc replacement with unknown unknowns. Cutting down your bundle size is a good goal, but this feels like much too steep of a tradeoff.
There is a trend in web dev right now to stop using jQuery because reasons. These reasons make sense if you use a framework and sometimes need to venture outside of it.
The problem is that the reasons are not often understood and then a tool like this comes along and now we can easily replace jQuery with a worse API adapter. Hooray!
The repo says it is for lightGallery, which is trying to offer a framework-agnostic lightbox component. Modern JS setups simply don't play nice with jquery.min.js, and it's a big download and hard to slice up. This helps folks reuse more old work like lightGallery. That's a pretty good reason, almost the best possible reason to build this, I think.
How it will likely be used is a different matter -- if you're a company trying to hire young devs and don't like to admit you have any legacy tech in the stack, then this is HOT code right now. Same if you need some bullshit targets to hit before the end of the quarter. Get on it!
I do feel the move against jquery comes from people that surpass it, which seems a shame. They had to use it once. It's like burning the rope for the people behind. Many low level users don't care about 30kb to get the $ shorthand as it's a habit they've had for a decade. It's funny almost to see the hoops people jump through not to use it. Like having a point to prove and not being able to prove it. Hey look, you can replace jquery with these 10 unmemorable esoteric steps.
> There is a trend in web dev right now to stop using jQuery because reasons.
One reason is that a lot of jquery’s bulk is compatibility concerns or BC in edges, if you decide that you don’t support old jQuery (mis)behaviour, older APIs, older browsers, … you can probably slim it down a fair bit.
A bunch of utilities you can also probably do without e.g. your jQuery.map, animations stuff, … jQuery is progressively deprecating more and more things, but it’s a harder sell to actually remove them.
I guess it might be useful as a first step if you want to reduce your dependencies on jQuery. First use this so you can drop the dependency, then slowly replace the functions you're using.
Seeing methods like addClass in "replace-jquery", I'm not fully satisfied. I could make Umbrella JS tiny (1/2 of the alternative listed elsewhere in the thread, Cash, and 10% the size of jQuery) because of heavy method reusal. For instance, in Umbrella JS addClass is just:
u.prototype.addClass = function () {
return this.eacharg(arguments, function (el, name) {
el.classList.add(name);
});
};
In "replace-jquery" you are already depending on `this.`, so why not making a couple of useful utils? Right now it is more verbose, and doesn't accept e.g. an array of classes or classes as arguments:
Cash JS's addClass (which is hidden behind toggleClass(cls, true)) is nice, it's bigger BUT that's because it's 3 implementations at once (addClass, removeClass, toggleClass). It properly uses a method to getSplitValues, which is very helpful and flexible:
fn.toggleClass = function ( this: Cash, cls: string, force?: boolean ) {
const classes = getSplitValues ( cls ),
isForce = !isUndefined ( force );
return this.each ( ( i, ele ) => {
if ( !isElement ( ele ) ) return;
each ( classes, ( i, c ) => {
if ( isForce ) {
force ? ele.classList.add ( c ) : ele.classList.remove ( c );
} else {
ele.classList.toggle ( c );
}
});
});
};
And I will spare you all jQuery's implementation, which is huge, but it can be seen here:
When you consider replacing `$.ajax` with fetch, you'll quickly found out that fetch is severely lacking with regards to:
* handling cookies,
* HTTP status codes (404, 403, etc),
* CORS,
* and even just simply readability when dealing with JSON.
jQuery's ajax (and its aliases like $.GET) handle all of these (edge cases? are these really edge cases?!) with aplomb, so you don't have to worry about it.
This is the issue with all of the jQuery alternatives, even cash (which does look pretty awesome); you start using them, and then development hits a halt because you suddenly realize that you actually need something that jQuery already does quite nicely, and has done so, quietly and politely, for more than a decade.
fetch('https://example.com').then(response => {
if (response.ok) {
// Response is 200-299
}
if (response.status === 404) {
// Status code specific handling
}
});
fetch('https://example.com').then(response => response.json()).then(json => {
// Do something with a parsed JSON object
});
I've used all of the above patterns regularly for several years now and never found any of them particularly cumbersome and certainly not lacking feature wise. If async/await syntax is available to you, it's even more succinct than the Promise style above.
With that said, I've not used $.ajax in anger for a good long while so I may be missing out, particularly as I note there have been API changes in newer versions of jQuery. Are there some specific use cases that you've found fetch to be particularly inept in dealing with?
These are great snippets! Although, your last snipped definitely shows the readability problem (even though you do a good job with it!)
Because each intermediate step is async as well, fetch can definitely turn into "callback hell" (really, promise hell) that the old callback style of JS used to be well-known for (and was resolved in large part by jQ-style chaining in the form of promises).
You might have a look at this link, especially the "Differences with jQuery":
"The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
"fetch() won’t send cross-origin cookies unless you set the credentials init option (to include)."
With regards to CORS, you have very limited options as well: no-cors, cors, same-origin. I've definitely had issues with CORS requests that didn't apply to XHR (and jQuery's AJAX by extension.)
"Note that mode: "no-cors" only allows a limited set of headers in the request:
"Accept
Accept-Language
Content-Language
Content-Type with a value of application/x-www-form-urlencoded, multipart/form-data, or text/plain"
For example:
"Note: Access-Control-Allow-Origin is prohibited from using a wildcard for requests with credentials: 'include'. In such cases, the exact origin must be provided; even if you are using a CORS unblocker extension, the requests will still fail."
Cash's maintainer here. I think needing $.ajax is a fairly niche thing (like ~nobody using vue/react/svelte is asking for a jQuery-like ajax function, the world moved to fetch), and it's not something that you'd suddenly stumble upon either.
Umbrella JS' creator here, I fully agree with that statement. You'd normally use something like Axios/Got/etc for a reusable API interface for API-heavy interfaces, and for simple cases it's not too complex to add e.g. `credentials: 'include'` for cookies.
Cash's maintainer here. I really don't think anybody can squeeze 50% out of the 6kb min+gzip code that make up Cash, quite a bit of thought went into squeezing as many bytes as possible out of it. (while still preserving a large degree of compatibility with jQuery, support for many methods and support for partial compilation)
Your comparison isn't quite fair, our toggleClass function does a lot more than a simple addClass, and writing it this way lowers in fact the total min+gzip size.
I'd be pretty impressed if you can shave just 1kb out of those 6kb to be honest.
Agreed! Not a fair comparison either way; if I compare Cash' addClass then it doesn't really "do anything" besides call the other method, so I'd say the toggleClass is representative of _the 3 methods together_, so 1/3rd? It's nice code :)
Shaving off 1kb out of 6kb of optimized GZIP code is probably impossible unless you missed something important.
I also went deep into optimizing Umbrella JS, it does a lot less than Cash so hence it's a lot smaller as well; but I went so far as comparing the GZIP output of calling `for ()` vs `forEach`, etc:
That's why I am not concerned of repeating e.g. `function() {}` (instead of the, back then, experimental () => {}) many times, since with gzip in my experience that gets all totally abstracted out.
Edit: edited the original, explaining that Cash it's doing it nicely!
It’s worth remembering that jQuery is a ~30kb “cost” for the end user. Once upon a time, that was a lot. And it was entirely prudent to question its necessity on the basis of load times and bandwidth consumption.
But now we live in a world where many common web pages have over 1000kb of resources on them. And nobody blinks an eyelid.
I'm not sure I buy this argument, one can't just say that ~30kb isn't a lot because 30 is a number perceived as low, would 30kb be justified for a library that allows you to toggle a class on a node? Of course it wouldn't, you need to measure what you are getting for 30kb.
I don't buy the second part of the argument either, you can load a 1000kb image on a blog post and that won't have nearly the same effect as loading 1000kb of JS. The JS needs to be parsed and executed and maybe the site doesn't even work without it, the image can probably be rendered progressively, can be decoded in another thread, nothing is really waiting on it to load, and if it doesn't load at all it's not the end of the world anyway.
With ~4kb you can have Preact, is jQuery Slim (~26kb) giving you ~6.5x times as much value as Preact really? Maybe it is, probably not.
For some context I maintain a ~6kb rewrite of a subset of jQuery (https://github.com/fabiospampinato/cash), which IMO is much better value proposition for many use cases.
I get this, but my website is 150kb on first load with a stale cache, most of which is a ~50kb JS blob, ~10kb CSS blob and ~80kb of fonts. All these static resources are served from the cloud edge with Cloudflare and aggressively cached in the browser. (All quantities gzipped.) Subsequent pages are typically less than 10kb and are served from a geographic location near to 80% of my country-specific audience.
The reality is my site (a discussion forum) has a substantially smaller footprint than just about any other similar site, let alone most Wordpress templates.
The size of jQuery is, for me, outstanding value for the efficiencies it gives me. I have not checked out Cash but the main reason I use jQuery is for the ajax scaffolding which most alternatives don't offer.
netflix.com (which doesn't even do anything): 1.6MB
CNN: 3.4MB
NYT: 4.1MB
Guardian: 5.7MB
IGN: 69.5MB
I still use jquery when I'm targeting ancient devices. Some of them still run Presto. Performance is fine, file size is fine - and drastically smaller than any of the sites above!
(I can see why one might not want jquery as a dependency of a dependency though.)
I used to work on low power/ancient devices running presto or blink. Performance was much better on opera 12.80 or so.
At some point I had to support an outside team working on external application running inside ours.
These guys used jQuery in ways it shouldn't be used when performance is an issue. Had to spend a few hours reducing their code to mostly CSS and just a tiny bit of honest to god js that even presto can understand and render.
Point is, it's really easy to overuse and that can have an effect on performance, even on presto
We may know the same devices - designed to run Flash Lite acceptably then made to just barely support HTML/JS with heroic engineering efforts later. Nobody believes how slow they are until they try.
I think if someone queries the DOM a lot on these things, especially in response to keypresses, they're in trouble whether or not they're doing it with jquery.
The lowest common denominator I had to deal with were some quite expensive, for the hardware, Arris devices. Roughly 10 times slower than a 2 core skylake and similar. I wish I was joking. As you say, people have no idea what slow is until they have to code for these.
I've played with some fairly decent machines as well. Quite a bit faster and cheaper but you can't just magically replacement millions of devices every time something new comes along
Like, I agree that if your web page loads a full MB of Javascript, eliminating JQuery should be the least of your concerns; it's a drop in the bucket, so optimize elsewhere. But web pages really shouldn't be loading 1 MB of Javascript in the first place.
It's so nice when I come across a website that's actually slim, I can really feel the difference...
But, we didn't used to have megabytes of JS on pages back when everyone used jquery.
I think what leads to pages with megabytes of JS is when people automatically assume that all projects must be a SPA built on one the popular everything-but-the-kitchen-sink frameworks.
This is especially true because now instead of all your dependencies using jQuery, they are each implementing a small subset of jQuery on their own.
The problem you thought you solved was actually made exponentially worse when those dependencies dependencies also rewrote some jQuery using different native methods
But it isn’t a one-way street. Using jquery can eliminate the need for other, lengthier blobs of code. In fact that’s exactly what this “vanilla JavaScript alternative” does.
With all the people replacing jQuery manually in their projects, you have to wonder whether the summed up costs of everyone running their own replacements is not larger than jQuery itself, once you use 3 or 4 such libraries :D
It’s been awhile since I’ve worked in the frontend space, but even 5 or 6 years ago if you were using a pretty popular hosted CDN of JQuery with a pretty recent version there was a pretty good chance it was already cached on the end user’s device when they hit your site. There’s always a lot of talk about Jquery’s size but I wonder how many users don’t even notice because they’ve already got it on their system.
There’s also of course the added cost of executing the library on the site but I’m guessing V8 and other JS engines have optimized the hell out of that too as to make it pretty negligible in terms of time difference.
Modern browsers partition caches by site nowadays - at least, Chrome started doing it last year[0], and Firefox followed soon after I think [1]. That means there's no longer any caching benefit for multiple websites using jQuery - each site will download and cache it separately.
Interesting. Reading Mozilla’s justification it does make a lot of sense, however it is kind of unfortunate to lose something that was pretty beneficial in terms of performance because of some bad actors. It’s not really viable to maintain a whitelist of trusted libraries either, and doing so would create a whole new class of problems.
Ideally CDN’s are powerful and ubiquitous enough these days that at least when you have the end user going and asking for JQuery from Cloudflare or whoever the CDN probably has a very fast location right next to them distance wise and that data should get over the wire pretty dang quickly. It’s still another performance hit though since you know at least the first time on the site they have to go and get it and if you are hosting your own stuff it might make more sense to just webpack everything together and hand it off to the CDN instead of having something like JQuery be on its own. Less overhead for opening another network request and all that.
From the first glance it replaces the jquery calls to some $utils implementation of the same methods. I guess final bundle contains only methods in $utils that are actually used in the code base, but it looks like re-implementation of jquery subset. Would not just using jquery with tree-shaking achieve similar effect? (not a JavaScript expert, so sorry if it is a dumb question).
Maybe another advantage of this is that new implementations target runtime version that is new enough to avoid most of the feature-checking and shims? Would be great if README addressed those questions.
jQuery is not tree-shakeable because it uses a chainable API. Like you don't import `toggleClass`, you access the `toggleClass` property of a jQuery instance, so it can't be tree-shaken off automatically.
Even if somebody made a bundler plugin for doing the heavy work jQuery can only be partially compiled with whole modules excluded, you can't exclude individual methods (e.g. you can't just remove `toggleClass`, you have to remove also `addClass`, `removeClass` etc.).
FYI I maintain a jQuery alternative that supports being partially compiled with individual modules turned off, but it requires manually listing them: https://github.com/fabiospampinato/cash
Tree shaking works well when you have ESM imports / exports.
jquery is/was usually loaded into the global scope. There is no standard way for tree shakers to deduct which parts of the library you are using.
Yes, browser APIs have matured significantly since jquery was new and such a library doesn't add much value anymore.
I agree it would be great if the README gave a bit more background. Whoever is looking to migrate away from jquery will be aware of the background though (and will likely appreciate this tool).
People are confused about what vanilla JavaScript means. If you’re replacing jQuery for another loose library you’re just fooling yourself and wasting time doing it.
This is akin to all those StackOverflow answers suggesting to use `any` for every TypeScript problem they encounter.
For example, your `on` method [1]:
- Doesn't support event delegation.
- Doesn't support event namespaces.
- Doesn't support receiving an argument mapping events to callbacks like jQuery also can.
- It seems to have subtle bugs, like the way the events string is split makes so that double consecutive spaces in it (which can happen as a result of a typo) will result in listening to the empty string event. Basically: 'foo bar'.split ( ' ' ) => ['foo', '', 'bar'] (there are two spaces between foo and bar).
The `on` method we are using in Cash [2] is a lot more convoluted than that. On one hand it requires more bytes, but on the other the chances of it behaving exactly like jQuery's are much higher. In fact we can also run jQuery's test suite with Cash to spot issues.
Feel free to ping me if you are interested in joining forces.
[0]: https://github.com/fabiospampinato/cash
[1]: https://github.com/sachinchoolur/replace-jquery#on
[2]: https://github.com/fabiospampinato/cash/blob/master/src/even...