43

I have often heard that jQuery has made some poor API decisions. Although jQuery is not my favourite library it's the library I've used most often and I find it hard to point out specific mistakes in the API design or how it could have been improved.

What parts of jQuery's API could have been done better, how could it have been implemented different and why would that different implementation be better?

The question extends to both low level individual details of the API and high level details of the API. We are only talking about flaws in the API rather then flaws in the high level design / purpose of the library, jQuery is still a DOM manipulation library centred around a selector engine.

Because of the necessity of API freezing in popular libraries, jQuery is stuck in it's current state and the developers are doing a great job. As can be seen by the recent .attr vs .prop change the developers do not have the flexibility to change any of their design decisions (which is a shame!).

One specific example I can think of would be

$.each(function(key, val) { })

vs

$.grep(function(val, key) { })

which is confusing enough that I have to double check what the parameters are frequently.

Please do not compare the jQuery library to full fledged frameworks like dojo and YUI and complain about lack of features.

Raynos
  • 166,823
  • 56
  • 351
  • 396

4 Answers4

41
  • .load() is overloaded with entirely different behavior depending on the arguments passed

  • .toggle() is overloaded with entirely different behavior depending on the arguments passed

  • too much overloading of the jQuery() function perhaps.

  • the .attr() you mentioned. The distinction from properties should have been immediate IMO.

  • .map( key,val ) but $.map( val,key ), and the this values are different.

  • non-standard selectors ought to have been kept out of Sizzle IMO. Javascript based selector engines should become obsolete in a number of years, and people hooked on the proprietary selectors will have a more difficult transition

  • poor method naming of methods like .closest() or .live(). What exactly do they do?

  • I recently discovered that you can't set the standard width and height attributes via the props argument when creating a new element. jQuery runs its own width and height methods instead. IMO, the spec attributes should have been given priority, especially since width and height can be set via css.

$('<img/>', { 
    css:{width:100, height:100},
    width:100, // <-- calls method, why?
    height:100, // <-- calls method, why?
});
  • $.get() and .get() are entirely different.

  • .get() and .toArray() are identical when passing no arguments

  • toArray() and $.makeArray() do effectively the same thing. Why didn't they give them the same name like .each() and $.each()?

  • two different event delegation methods. .delegate() the sensible one, and .live() the magical "wow, it just works!" one.

  • .index() is overloaded with 3 behaviors, but their differences can be confusing

 // v---get index   v---from collection (siblings is implied)
$('selector').index();
 // v---from collection   v---get index
$('selector').index(element);
 // v---get index      v---from collection
$('selector').index('selector');

The first one is understandable if you remember that it only operates on the first element

The second one makes the most sense since jQuery methods usually operate on an entire collection.

The third one is entirely confusing. The method gives no indication of which selector is the collection and which selector represents the element whose index you want from the collection.

Why not just eliminate the third one, and have people use the second one like this:

 // v---from collection      v---get index
$('selector').index( $('selector') );

This way it fits more closely with the rest of jQuery where .index() operates on the entire collection.

Or at least reverse the meaning of the selectors to fit in better:

 // v---from collection   v---get index
$('selector').index('selector');

Here's another to think about anyway.

I have some concerns with jQuery's event handling/data storage system. It is praised because it doesn't add functions to on[event] properties that can close around other elements, creating memory leaks in IE. Instead it places a lightweight expando property, which maps to an entry in jQuery.cache, which holds handlers and other data.

I believe it then attaches a handler with in turn invokes the handler that you assigned. Or something like that.

Whatever the system is doesn't really matter. The point is that the connection between the element(s) and the jQuery.cache is that expando.

Why is that a big deal? Well philosophically jQuery is not a framework; it is a library. It would seem that as a library you should be able to use or not use the jQuery functions without concern for negative effects. Yet if you go outside jQuery when removing elements from the DOM, you've orphaned any handlers and other data associated with those elements via the expando, creating a nice and fully cross-browser memory leak.

So for example, something as simple as el.innerHTML = '' could be very dangerous.

Couple this with the jQuery.noConflict() feature. This enables developers to use jQuery with other libraries that utilize the $ global namespace. Well what if one of those libraries deletes some elements? Same problem. I have a feeling that the developer that needs to use a library like Prototypejs along side jQuery probably doesn't know enough JavaScript to make good design decisions, and will be subject to such a problem as I've described.


In terms of improvements within the intended philosophy of the library, as far as I know, their philosophy is "Do more, write less" or something. I think they accomplish that very well. You can write some very concise yet expressive code that will do an enormous amount of work.

While this is very good, in a way I think of it as something of a negative. You can do so much, so easily, it is very easy for beginners to write some very bad code. It would be good I think if there was a "developer build" that logged warnings of misuse of the library.

A common example is running a selector in a loop. DOM selection is very easy to do, that it seems like you can just run a selector every time you need an element, even if you just ran that selector. An improvement I think would be for the jQuery() function to log repeated uses of a selector, and give a console note that a selector can be cached.

Because jQuery is so dominant, I think it would be good if they not only made it easy to be a JavaScript/DOM programmer, but also helped you be a better one.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • `map` is the one I immediately thought of. Really peculiar design decision. – lonesomeday May 19 '11 at 19:46
  • You've tackled what's bad, could you expand it with non-trivial examples of API improvements. – Raynos May 19 '11 at 19:48
  • @lonesomeday that explains why I didn't know the `.map` signature off hand. I used grep/each as an example because there at least a bit consistent :( – Raynos May 19 '11 at 19:51
  • @patrick_dw Please tell me that last example is not true :( – Raynos May 19 '11 at 19:53
  • @Raynos: I'm not sure what you mean. Do you mean how those items could have been improved? – user113716 May 19 '11 at 19:54
  • @patrick_dw the question was half about what are the bad decisons in the API and what could have been improved. Apart from taking out the bad decisions I find it hard to see what can be improved because I'm so familiar with jQuery. I mean how any items could be improved non-trivially. (You don't nee to say the obvious ;) ). – Raynos May 19 '11 at 19:59
  • @Raynos: I don't mean to seem obtuse, but you mean what can be improved *aside* from correcting the issues I mentioned? In other words, you're asking how jQuery could be otherwise improved as a library? – user113716 May 19 '11 at 20:02
  • @patrick_dw Yes, but without changing it's purpose or the general philosophy behind it. A valid answer may be that there are no improvements in this regard. (I can't see any.) – Raynos May 19 '11 at 20:04
  • @Raynos: I see. I'm going to leave one more negative that I thought of. In terms of potential improvements within the intention of the library, well I'll try to give a short opinion in a minute. – user113716 May 19 '11 at 20:10
  • 1
    +1 great point about being obtrusive with the `expando` property. This is something that's not often talked about when we talk about jQuery's compatibility with other libraries. – Anurag May 19 '11 at 20:38
  • Another +1 for `.cache` memory leak and the `jQuery.dev.js`, I think there is a [jQuery lint](http://james.padolsey.com/javascript/jquery-lint/) for this. – Raynos May 19 '11 at 20:39
  • @Anurag: Several months ago there was a user here who didn't understand why handlers didn't work after destroying and recreating a large part of the DOM. I made the mistake of first indicating that the elements needed to be rebound after being destroyed. The trouble was tat they were being destroyed via `innerHTML`! Repeatedly! Once the user had a "solution", he didn't want to hear my emphatic warning that not only was it inefficient to destroy/create the DOM like that, but that all that data was being orphaned. It just worked, and that was fine. I learned to not lead with the easy fix. – user113716 May 19 '11 at 20:44
  • @Raynos: I added a few more peeves. Anyway, yes there's jQuery Lint. IMO that sort of thing should be *part* of jQuery. jQuery is *huge* now, and I think they have some responsibilities. They're a bit sloppy with their releases and bug introductions, and I think some sort of enforcement of proper use patterns should be an integral part of the library. – user113716 May 19 '11 at 20:47
  • ...I mean just imagine if they simply logged a console warning when a selector didn't match any elements, suggesting that the developer ensure that the DOM is ready. Imagine how many questions that would resolve. – user113716 May 19 '11 at 20:49
  • @patrick_dw what about some kind of clever algorithm to figure out whether your trying to use ajax synchronously ;) That would reduce the jQuery questions by a third. – Raynos May 19 '11 at 20:51
  • @Raynos: StackOverflow would go out of business! ;o) Thanks for this question. It has been very therapeutic. ;o) – user113716 May 19 '11 at 20:59
  • @patrick_dw a legitimate excuse to complain about jQuery without ranting _and being productive too_ ! – Raynos May 19 '11 at 21:02
  • @Raynos: Frankly, I'm amazed that I haven't been down-voted. I think your phrasing of the question was perfectly balanced, making my rant seem productive! – user113716 May 19 '11 at 21:09
  • @patrick_dw "wow, it just works!" `.live` makes me die a little inside :(. I'm shocked I havn't been downvoted for "insulting jQuery" I was considering titling it "API Design and why jQuery is bad" but it would have been closed long ago if I did ;) – Raynos May 19 '11 at 21:12
  • Great job. Attr, in particular, is a disaster. It's still broken. –  May 19 '11 at 21:21
  • @Matt M: Thanks, and agreed regarding attr. – user113716 May 19 '11 at 21:48
  • `attr()` is even more broken now. The `prop()` thing has turned into a total fiasco. – Tim Down May 19 '11 at 23:05
  • @Tim Down: Do you mean because of the functionality that was removed from `.attr()`? As I understand, it's all back in now. Or is there some other issue? – user113716 May 19 '11 at 23:08
  • @Raynos: I just notice your comment *"Please tell me that last example is not true"* from way back. I'm curious, were you talking about the `width` and `height` issue? – user113716 May 19 '11 at 23:09
  • @patrick_dw yes. I was complaining about that (bad) design decision. I would spend hours debugging that then go into a murderous rage. – Raynos May 19 '11 at 23:17
  • @Raynos: I was fuming because of that, but not because of the bug. I found about the issue from [this question](http://stackoverflow.com/questions/6037231/bug-in-jquery-dom-element-builder-for-images-with-width-height/) a couple days ago. I [filed a bug report](http://bugs.jquery.com/ticket/9323) in response to it suggesting a possible fix without breaking functionality. The first two triage guys politely acknowledged it, but said it wasn't worth fixing. Fine. Then a third one came in who pissed me off enough for me to cancel my account. I won't be spending any more time on jQuery bug reports. – user113716 May 19 '11 at 23:31
  • @patrick: `attr()` is something like it was before, only it now usually uses attributes rather than properties and behaves differently from before with boolean attributes. `prop()` use properties and is therefore the more sensible method, yet the message from Resig (the Ressage, maybe?) is to prefer `attr()`, meaning that `prop()` is just sitting there with no clearly defined purpose. They were very blasé about making the change 1.6, even though it was obvious it would break loads of sites, and then panicked and fudged it in 1.6.1, meaning the situation is now worse. – Tim Down May 19 '11 at 23:41
  • @patrick: The problem they were trying to solve, and rightly, was that they had one ill-defined method, `attr()`. They attempted to split out the two concepts (properties and attributes) it tried to present as one, hurriedly backtracked somewhat and have ended up with two ill-defined methods. – Tim Down May 19 '11 at 23:46
  • @Tim Down: Yeah I knew they backtracked in 1.6.1, but I don't have any real experience with the new `.attr()` yet. I thought they effectively restored the old `.attr()`. I agree that `.prop()` is a good thing, and they should have never combined the concepts. But it should really make a person wonder, did they honestly not know that radically changing `.attr()` would break a ton of code? How could they have *not* anticipated that? And the scramble to reverse it just makes them look incompetent, or indecisive at best. They should've taken the Apple approach. *"We're changing it. Deal with it!"* – user113716 May 19 '11 at 23:59
  • @patrick: Agreed. I get to be a little bit smug: http://stackoverflow.com/questions/4456231/retrieving-html-attribute-values-the-dom-0-way/4456805#4456805 (see final comment on that answer). – Tim Down May 20 '11 at 00:05
  • @Tim Down: Bulls-freaking-eye. Enjoy that bit of smugness. You've earned it. – user113716 May 20 '11 at 00:15
  • ...they should've just stuck with it. jQuery breaking code in a release is nothing new. – user113716 May 20 '11 at 00:16
8

The way jQuery handles collections vs single elements can be confusing.

Say if we were to update some css property on a collection of elements, we could write,

$('p').css('background-color', 'blue');

The setter will update the background color of all matching elements. The getter, however, assumes that you are only interested in retrieving the value of the first element.

$('p').css('background-color')

MooTools would return an array containing the background colors of each matching element, which seems more intuitive.

The naming conventions for jQuery promote conciseness instead of clarity. I like Apple's strategy in naming things:

It's better to be clear than brief.

And here's an example of a method name from a mutable array class (NSMutableArray) in Objective-C.

removeObjectAtIndex:(..)

It's not trying to be clever about what's getting removed or where it's getting removed from. All the information you need to know is contained in the name of the method. Contrast this with most of jQuery's methods like after and insertAfter.

If somebody can intuitively figure out what after or insertAfter does without reading the docs or the source code, then that person is a genius. Unfortunately, I'm not one - and to-date, I still have to go to the documentation to figure out what the hell gets placed where when using these two methods.

Anurag
  • 140,337
  • 36
  • 221
  • 257
  • +1 Good one. I've never understood the usefulness of only returning the first result. – user113716 May 19 '11 at 20:23
  • 1
    +1 or what does `after` and `insertAfter` do. Also `append` and `appendTo` are non-trivial. – Raynos May 19 '11 at 20:30
  • 1
    `after` and `insertAfter` (and their `before` counterparts), another great example. Those drive me nuts! – user113716 May 19 '11 at 20:36
  • about `It's better to be clear than brief.` - there is a big difference between compiled languages and interpreted languages. In latter, all function names are in production app! – Jacek Kaniuk Aug 06 '11 at 13:43
  • @Jacek - That makes absolutely no sense. Care to explain further? – Anurag Aug 06 '11 at 21:43
  • in web app - size is the key, and every call to a function with long name would add overhead to data send between browser and server (upon loading a script) – Jacek Kaniuk Aug 06 '11 at 21:46
  • 1
    That is a ridiculous argument. Keeping code size does not mean that I will name my variables `a`, `b`, `c`, `d`, etc. That is the job of the compiler, or [minification](http://en.wikipedia.org/wiki/Minification_(programming)) if you're speaking of interpreted languages, and specifically JavaScript. – Anurag Aug 06 '11 at 21:50
  • Look around - how many minified scripts do you see besides libraries and big sites (and even libs are frequently in developers version) ? – Jacek Kaniuk Aug 06 '11 at 21:52
  • Does it really matter *how many* minified scripts are out there? The point still remains - why should a programmer write obfuscated code, when clearly there are better tools out there to do that. – Anurag Aug 06 '11 at 21:57
  • It's not about programmer but about library developer. He has to mind how library would be used. – Jacek Kaniuk Aug 07 '11 at 16:45
  • 1
    A library developer is a programmer too. Writing high quality code is just as important for a user of a library, as it is for the developer of that library. In fact, choosing good descriptive variable names is so important that one of the most popular books on software construction ([Code Complete](http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670)) has a complete chapter just on choosing good names for variables. – Anurag Aug 08 '11 at 04:04
  • +1 for "It's better to be clear than brief." Anyway almost everybody uses automatic completion when coding, and one can use minification when deploying if needed. – Luc125 Mar 13 '12 at 14:19
5

patrick dw hit most of the points in his (fantastic) answer. Just to add to his collection with a few other examples.

An API is supposed to be consistent; and jQuery succeeds in a lot of areas (being very consistent for returning a jQuery object/ get value, as expected in a lot of cases). In other situations however, it doesn't do so well.

Method Names As already pointed out by patrick; closest() is a crap method name. prev(), and next() appear to most as if they do the job prevAll() and nextAll() actually provide.

delay() confuses a lot of people: In the following example, which do you expect to happen? (what actually happens?)

$('#foo').hide().delay(2000).slideDown().text('Hello!').delay(2000).hide();

Method Arguments A lot of the tree traversal functions are inconsistent with what they accept; they all accept a mixture of selectors, jQuery objects and elements, but none are consistent; which is bad considering they all do similar jobs. Check out closest(), find(), siblings(), parents(), parent() and compare the differences!

Internally, the "core" of jQuery originally contained lots of intertwined methods, that the dev team have struggled to split up (and done really well to), over the past releases. Internal modules such as css, attributes, manipulation and traversing all used to be bundled in the same big package.

Community
  • 1
  • 1
Matt
  • 74,352
  • 26
  • 153
  • 180
  • 1
    the `

    ` never gets hidden in that example could you explain why? and +1 for the developers doing a great job with what could be considered a bad start.

    – Raynos May 19 '11 at 20:49
4

Included in jquery:

.post()
.get()
.getScript()
.getJSON()
.load()

Not in jQuery:

$.getXML();
$.headXML();
$.postXML();
$.putXML();
$.traceXML();
$.deleteXML();
$.connectXML();
$.getJSON();
$.headJSON();
$.postJSON();
$.putJSON();
$.traceJSON();
$.deleteJSON();
$.connectJSON();
$.headScript();
$.postScript();
$.putScript();
$.traceScript();
$.deleteScript();
$.connectScript();
$.getHTML();
$.headHTML();
$.postHTML();
$.putHTML();
$.traceHTML();
$.deleteHTML();
$.connectHTML();
$.getText();
$.headText();
$.postText();
$.putText();
$.traceText();
$.deleteText();
$.connectText();
$.head();
$.put();
$.trace();
$.delete();
$.connect();

Why does this bother me? Not because we don't have the above methods in the library, that's just dumb and I'd hate if we ever did (plus, most won't work with browsers), but what I hate is these short-hand methods all over the place: $.post(); Sends an ajax request via post.

Know what covers off everything in this list? The one function that isn't short-hand, $.ajax, it's full featured and covers off everything, you can even configure it to store defaults and in essence create all of these short-hands. You can create your own methods if you wish to call that call to ajax (which is what these all do).

Here's where it's really REALLY annoying.

Somebody writes all their code using shorthand:

$.getJSON(
  'ajax/test.html', 
   function(data) {
     $('.result').html(data);
   }
);

Okay, oh wait, we want to change that to an XML feed we get from a post, also we need to make something happen before and after post. Oh, lets just switch to the non-short-hand method

$.ajax({
  type: 'POST',
  url: url,
  data: data,
  success: success
  dataType: dataType
});

Oh wait, I can't just replace the word, the whole structure is all over the place.

This, and especially for things like $.load(), cross lines into why PHP has such a hated API, they're trying to do things they shouldn't do.

To extend this beyond AJAX calls, look at the animation portion. We have calls to slideUp, slideDown, fadeIn, fadeOut, fadeToggle, show, and hide. Why? Where's my slide left, slide right, slide in, slide out, teleport, and every other thing we can think up? Why not just stick to $.animate() and let someone write a plugin if we want those effects. Infact, someone did write a plugin, jQueryUI extends animations. This is just crazy, and leads to people not knowing that some effects create CSS rules on their divs that end up goofing up what they want to do later on.

Keep it pure, keep it simple.

Incognito
  • 20,537
  • 15
  • 80
  • 120
  • Who uses `TRACE` and `CONNECT`. Cmon ;) – Raynos May 19 '11 at 20:19
  • To be honest, most browsers disallow anything but get and post, I was making the list really to make it obvious how this philosophy fails when scaling. – Incognito May 19 '11 at 20:26
  • @user257493 +1 for jQueryUI extending animate and me wondering why jQuery animate breaks without jQueryUI. – Raynos May 19 '11 at 20:29
  • I'd like to posit that jQuery is "shorthanding" commonly used methods. It's far more likely that someone is going to use `$.getJSON` than `$.deleteJSON` (or many other of the listed non-existant functions - even the XML ones (these days)). Similarly, `slideDown` is a lot more likely to be used than `slideLeft` – Ryan Kinal May 20 '11 at 12:38
  • That's exactly what they are doing and has been confirmed, but I'm suggesting it's bad practice. – Incognito May 20 '11 at 13:11
  • 1
    Yet it follows their philosophy of "Do more with less". They literally have shortcuts all over the place. `.click` is a shortcut for `.bind('click')`, for instance. – Ryan Kinal May 20 '11 at 15:19