181

I heard several times that jQuery's strongest asset is the way it queries and manipulates elements in the DOM: you can use CSS queries to create complex queries that would be very hard to do in regular javascript . However , as far as I know, you can achieve the same result with document.querySelector or document.querySelectorAll, which are supported in Internet Explorer 8 and above.

So the question is this: why 'risk' jQuery's overhead if its strongest asset can be achieved with pure JavaScript?

I know jQuery has more than just CSS selectors, for example cross browser AJAX, nice event attaching etc. But its querying part is a very big part of the strength of jQuery!

Any thoughts?

John Slegers
  • 45,213
  • 22
  • 199
  • 169
Joel Blum
  • 7,750
  • 10
  • 41
  • 60
  • 4
    (1) DOM traversal/modification is much quicker and easier with jQuery. (2) It adds its own selectors that won't work in the `querySelector` methods. (3) Making AJAX calls is much quicker and easier with jQuery. (4) Support in IE6+. I'm sure there are many more points that could be made too. – James Allardice Jul 16 '12 at 11:45
  • 16
    (5)... the shorthand $() for lazy typists is a must. – Dexter Huinda Jul 16 '12 at 11:49
  • 4
    easier yes , why quicker ? jQuery translates to regular javascript as far as I know ... – Joel Blum Jul 16 '12 at 11:51
  • @James It's not quicker or at least - only in a few cases. It's the crossbrowser compatibility that makes the huge difference. – Christoph Jul 16 '12 at 11:53
  • @Christoph - By "quicker" I meant "quicker to implement", not that the actual calls themselves were faster e.g. no need for all the mess involved in obtaining a cross-browser `XMLHttpRequest` object. – James Allardice Jul 16 '12 at 11:54
  • This is like questioning why use `C` instead of `assembly`, when you can handle things in `assembly`, much faster execution [machine-language], and much less bytes used [object code], although requires lots of source code. – Dexter Huinda Jul 16 '12 at 12:00
  • 4
    @JamesAllardice—"all that mess" for cross-browser XMLHttpRequest is maybe [30 lines of code](http://www.jibbering.com/2002/4/httprequest.html) that you write once and put in your own library. – RobG Jul 16 '12 at 12:38
  • 7
    @RobG - Yeah, I'm not saying just use jQuery if that's all you're trying to use it for. It's just one of the benefits. If you need easy DOM traversal, AJAX and `querySelectorAll`, and you need it all to work in older browsers, then jQuery is an obvious choice. I'm not saying that you should use it [like this](http://www.doxdesk.com/img/updates/20091116-so-large.gif). – James Allardice Jul 16 '12 at 12:41

12 Answers12

142

document.querySelectorAll() has several inconsistencies across browsers and is not supported in older browsersThis probably won't cause any trouble anymore nowadays. It has a very unintuitive scoping mechanism and some other not so nice features. Also with javascript you have a harder time working with the result sets of these queries, which in many cases you might want to do. jQuery provides functions to work on them like: filter(), find(), children(), parent(), map(), not() and several more. Not to mention the jQuery ability to work with pseudo-class selectors.

However, I would not consider these things as jQuery's strongest features but other things like "working" on the dom (events, styling, animation & manipulation) in a crossbrowser compatible way or the ajax interface.

If you only want the selector engine from jQuery you can use the one jQuery itself is using: Sizzle That way you have the power of jQuerys Selector engine without the nasty overhead.

EDIT: Just for the record, I'm a huge vanilla JavaScript fan. Nonetheless it's a fact that you sometimes need 10 lines of JavaScript where you would write 1 line jQuery.

Of course you have to be disciplined to not write jQuery like this:

$('ul.first').find('.foo').css('background-color', 'red').end().find('.bar').css('background-color', 'green').end();

This is extremely hard to read, while the latter is pretty clear:

$('ul.first')
   .find('.foo')
      .css('background-color', 'red')
.end()
   .find('.bar')
      .css('background-color', 'green')
.end();

The equivalent JavaScript would be far more complex illustrated by the pseudocode above:

1) Find the element, consider taking all element or only the first.

// $('ul.first')
// taking querySelectorAll has to be considered
var e = document.querySelector("ul.first");

2) Iterate over the array of child nodes via some (possibly nested or recursive) loops and check the class (classlist not available in all browsers!)

//.find('.foo')
for (var i = 0;i<e.length;i++){
     // older browser don't have element.classList -> even more complex
     e[i].children.classList.contains('foo');
     // do some more magic stuff here
}

3) apply the css style

// .css('background-color', 'green')
// note different notation
element.style.backgroundColor = "green" // or
element.style["background-color"] = "green"

This code would be at least two times as much lines of code you write with jQuery. Also you would have to consider cross-browser issues which will compromise the severe speed advantage (besides from the reliability) of the native code.

Nate
  • 18,752
  • 8
  • 48
  • 54
Christoph
  • 50,121
  • 21
  • 99
  • 128
  • 35
    What sort of inconsistencies does `querySelectorAll` have between browsers? And how would using jQuery solve this, since jQuery *uses* `querySelectorAll` when available? –  Jul 16 '12 at 12:10
  • 3
    True, one line of code can contain chains of endless codes which can be very annoying during debugging. – Dexter Huinda Jul 16 '12 at 12:13
  • @amnotiam I found a testsuite somewhere which showed some minor bugs in some browser, unfortunately i can't find it anymore. Maybe it's already been fixed. It is reliable in many cases, that's true. And sizzle takes it, yes but it also falls back to other methods like getElementById due to speed issues. – Christoph Jul 16 '12 at 12:20
  • If you happen to come across it, drop me a note. I'd be interested to see it. I do know that IE modifies certain attributes that it shouldn't when a related property is changed. This can have an impact on attribute selectors. –  Jul 16 '12 at 12:23
  • @amnotiam—HTML5 specifies that IDLAttribues (aka DOM properties) and HTML attributes [must reflect each other](http://dev.w3.org/html5/spec/single-page.html#reflecting-content-attributes-in-idl-attributes). The latest browsers seem to conform (e.g. Firefox 13, Safari 5). – RobG Jul 16 '12 at 12:50
  • @RobG: Hmmm... I'm not observing this behavior in FF 13 on Linux. Maybe I'm doing something wrong. –  Jul 16 '12 at 13:03
  • @amnotiam—I just set an attribute using *setAttribute* then read it back using the property name and vice versa. Didn't test an attribute in the markup, perhaps I should. – RobG Jul 17 '12 at 02:22
  • @RobG: Weird. I did the same thing, but wasn't able to observe the change. Not a feature I could rely on right now anyway, but will be something to keep my eye on. Anyway, thanks for letting me know about it. –  Jul 17 '12 at 02:47
  • Yes, unreliable. From memory, older versions of Firefox and Safari didn't modify attributes when properties were modified, but they did update properties when attributes were changed. IE always kept them together, which was seen as non-conforming. I guess they changed the rules so they are conforming. Helps to be a contributor to the W3C standards process. :-) – RobG Jul 18 '12 at 05:39
  • @squint I now discovered one of the general problems of QS again and added it to the answer. – Christoph May 30 '13 at 08:13
  • @Christoph Just randomly came across this question, and saw your fiddle showing the "scope" problem. I wouldn't call it a "problem". jQuery has changed the way we think about selecting elements. The `querySelectorAll()` result makes sense if you consider how it's supposed to be evaluated. It's saying "find a span that is a descendant of any div", yet jQuery's `.find()` method is saying "find a span that is a descendant of my descendant divs". I think `querySelectorAll()` is "correct" in the sense that it is evaluating things from right to left (like CSS), but it normally isn't what we expect – Ian Jun 13 '13 at 14:42
  • @Christoph If you look at http://jsfiddle.net/YmGXs/ , although silly to include an `id` in a `querySelectorAll()` call, the numbers match up – Ian Jun 13 '13 at 14:42
  • @Ian I know what you mean. It certainly is a point one could discuss over all day long. Take a look at my fiddle again. The whole sense of scoping is countered with the qS logic. You just do not expect to find a child span enclosed by a div, because *given `#foo` as scope*, there is *no* such element matching this criteria. Using the documentroot as scope regardless which element you are running the selector on is counterintuitive and *imo* wrong. Also you might wanna have a look at [John Resig's querySelector Article](http://ejohn.org/blog/thoughts-on-queryselectorall/). – Christoph Jun 13 '13 at 15:06
  • @Christoph Of course, I just wanted to say something and hear what you/anyone had to say about it. When you say it like that, it makes more sense. I guess I keep thinking of CSS selectors, which have no "scope", so my example makes more sense to me. But as soon as you introduce a scope, you can't exactly follow those same rules, so your argument is more valid. And thanks for that article, I'll definitely read it :) As I think about all this more, I struggle to choose whether I think it's "right" or "wrong" (not that it actually matters) – Ian Jun 13 '13 at 15:15
  • @Ian Interesting is also the [official document](http://www.w3.org/TR/selectors-api/#context-node), especially §6.2 where the context node is stated explicitely (and that the returned node are inside it's subtree) and §6.4 where it is stated that the selector is always evaluated in the context of the entire DOM tree. – Christoph Jun 13 '13 at 15:50
  • The question was about css selectors, not about the styling of selected nodes. As you can see here: http://jsfiddle.net/bmY7n/ the selecting part is not longer (in terms of lines of code) than jQuery's approach. – Vanuan Jul 16 '13 at 14:08
  • 2
    "2) iterate over the array of childnodes via some (possibly nested or recursive) loops and check the class" << This is a total crap. You can use querySelectorAll on the element in the previous step. – Vanuan Jul 16 '13 at 14:15
  • 5
    @Vanuan this *might* not be necessary, but if you would have read my answer thoroughly you would have noticed, that querySelector has a serious scoping problem wich might give you a lot of false positives when used in the way you propose. Nonetheless while your are free to up- or downvote for some nitpicky reason, I think it's not a reason to use rude language. – Christoph Jul 16 '13 at 16:39
  • @Christoph I'm really sorry if I offended you. – Vanuan Jul 16 '13 at 21:02
  • The native version is **way** more faster. You should check out some jsperf comparisons ... – Pascalius Feb 01 '14 at 00:00
  • @Pascalius Of course the native version is much faster, that was never part of the discussion. Did I mention anything opposite? – Christoph Feb 03 '14 at 08:49
  • @Christoph Yes, actually you mention this in your last sentence, that it would be 2 times the code and possibly slower ;) – Pascalius Feb 03 '14 at 10:31
  • @Pascalius well, this possibly slower does not refer to the selecting part but to the whole process of selection, filtering and setting attributes. Feel free to create a jsperf for that, I will link it in my answer and revise the "possibly slower" if you like. – Christoph Feb 03 '14 at 11:13
  • 1
    @Christoph There you go [http://jsperf.com/jquery-vs-native-selector-and-element-style](http://jsperf.com/jquery-vs-native-selector-and-element-style). Like I mentioned, there are _huge_ differences. It's more than 10 times faster. – Pascalius Feb 03 '14 at 12:30
  • @Pascalius Thanks, I'll add this to the answer. Nonetheless it's not truly representable because jQuery strength is not speed but crossbrowser compatibility. If one adds all the shims and conditions to make it crossbrowser compatible in the native code (yours won't work in IE<10), one easily ends with the same speed (or worse). – Christoph Feb 03 '14 at 12:45
  • 3
    @Christoph Since this is easy, I added compatiblity for IE8 and above. Still huge speed advantages (5-20 times). That the code will run slower in old browser like IE8 is just a wrong assumption. – Pascalius Feb 03 '14 at 13:15
70

If you are optimizing your page for IE8 or newer, you should really consider whether you need jquery or not. Modern browsers have many assets natively which jquery provides.

If you care for performance, you can have incredible performance benefits (2-10 faster) using native javascript: http://jsperf.com/jquery-vs-native-selector-and-element-style/2

I transformed a div-tagcloud from jquery to native javascript (IE8+ compatible), the results are impressive. 4 times faster with just a little overhead.

                    Number of lines       Execution Time                       
Jquery version :        340                    155ms
Native version :        370                    27ms

You Might Not Need Jquery provides a really nice overview, which native methods replace for which browser version.

http://youmightnotneedjquery.com/


Appendix: Further speed comparisons how native methods compete to jquery

Pascalius
  • 14,024
  • 4
  • 40
  • 38
  • nice overview though some of the code examples are wrong... E.g. `$(el).find(selector)` is not equal to `el.querySelectorAll(selector)` and the performance of native methods is often quite horrible: http://stackoverflow.com/q/14647470/1047823 – Christoph Feb 04 '14 at 08:42
  • @Christoph Can you elaborate, why you think the methods are different? Of course there are edge cases where jquery can perform better, but I haven't seen one for DOM-Manipulation. – Pascalius Feb 04 '14 at 09:50
  • 1
    No need to elaborate further, just read my answer, look at the fiddle and the article I linked to. Also, (at least atm) most of the native Array methods are inferior in speed compared to a naive js implementation (like I linked in the question in my first comment). And these are not edge cases, but rather but the standard case. But again, the main focus of this question was not the speed. – Christoph Feb 04 '14 at 12:20
  • 2
    @Christoph Of course, these methods are not 100% equal and jquery often provides more convenience. I updated the answer to show that this is just an edge case, I actually couldn't find any other case where jquery performed better. There is no main focus of the question. – Pascalius Feb 04 '14 at 14:57
  • +1 Excellent answer! I've been slowly replacing old jQuery code with raw JavaScript over the past 4 or 5 years wherever and whenever possible.. Of course, jQuery is great for some things and I use it for _those_ things when I feel I get a solid benefit. – Yes Barry Mar 26 '14 at 04:52
  • +1 Interesting that the native implementation of `Array.filter` is still lagging so far behind `$.grep` – jasonscript Jun 09 '14 at 05:15
25

To understand why jQuery is so popular, it's important to understand where we're coming from!

About a decade ago, top browsers were IE6, Netscape 8 and Firefox 1.5. Back in those days, there were little cross-browser ways to select an element from the DOM besides Document.getElementById().

So, when jQuery was released back in 2006, it was pretty revolutionary. Back then, jQuery set the standard for how to easily select / change HTML elements and trigger events, because its flexibility and browser support were unprecedented.

Now, more than a decade later, a lot of features that made jQuery so popular have become included in the javaScript standard:

These weren't generally available back in 2005. The fact that they are today obviously begs the question of why we should use jQuery at all. And indeed, people are increasingly wondering whether we should use jQuery at all.

So, if you think you understand JavaScript well enough to do without jQuery, please do! Don't feel forced to use jQuery, just because so many others are doing it!

John Slegers
  • 45,213
  • 22
  • 199
  • 169
9

That's because jQuery can do much more than querySelectorAll.

First of all, jQuery (and Sizzle, in particular), works for older browsers like IE7-8 that doesn't support CSS2.1-3 selectors.

Plus, Sizzle (which is the selector engine behind jQuery) offers you a lot of more advanced selector instruments, like the :selected pseudo-class, an advanced :not() selector, a more complex syntax like in $("> .children") and so on.

And it does it cross-browsers, flawlessly, offering all that jQuery can offer (plugins and APIs).

Yes, if you think you can rely on simple class and id selectors, jQuery is too much for you, and you'd be paying an exaggerated pay-off. But if you don't, and want to take advantage of all jQuery goodness, then use it.

MaxArt
  • 22,200
  • 10
  • 82
  • 81
8

jQuery's Sizzle selector engine can use querySelectorAll if it's available. It also smooths out inconsistencies between browsers to achieve uniform results. If you don't want to use all of jQuery, you could just use Sizzle separately. This is a pretty fundamental wheel to invent.

Here's some cherry-pickings from the source that show the kind of things jQuery(w/ Sizzle) sorts out for you:

Safari quirks mode:

if ( document.querySelectorAll ) {
  (function(){
    var oldSizzle = Sizzle,
      div = document.createElement("div"),
      id = "__sizzle__";

    div.innerHTML = "<p class='TEST'></p>";

    // Safari can't handle uppercase or unicode characters when
    // in quirks mode.
    if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
      return;
    }

If that guard fails it uses it's a version of Sizzle that isn't enhanced with querySelectorAll. Further down there are specific handles for inconsistencies in IE, Opera, and the Blackberry browser.

  // Check parentNode to catch when Blackberry 4.6 returns
  // nodes that are no longer in the document #6963
  if ( elem && elem.parentNode ) {
    // Handle the case where IE and Opera return items
    // by name instead of ID
    if ( elem.id === match[3] ) {
      return makeArray( [ elem ], extra );
    }

  } else {
    return makeArray( [], extra );
  }

And if all else fails it will return the result of oldSizzle(query, context, extra, seed).

KGZM
  • 414
  • 3
  • 5
6

In terms of code maintainability, there are several reasons to stick with a widely used library.

One of the main ones is that they are well documented, and have communities such as ... say ... stackexchange, where help with the libraries can be found. With a custom coded library, you have the source code, and maybe a how-to document, unless the coder(s) spent more time documenting the code than writing it, which is vanishingly rare.

Writing your own library might work for you , but the intern sitting at the next desk may have an easier time getting up to speed with something like jQuery.

Call it network effect if you like. This isn't to say that the code will be superior in jQuery; just that the concise nature of the code makes it easier to grasp the overall structure for programmers of all skill levels, if only because there's more functional code visible at once in the file you are viewing. In this sense, 5 lines of code is better than 10.

To sum up, I see the main benefits of jQuery as being concise code, and ubiquity.

Dom Day
  • 2,542
  • 13
  • 12
6

Here's a comparison if I want to apply the same attribute, e.g. hide all elements of class "my-class". This is one reason to use jQuery.

jQuery:

$('.my-class').hide();

JavaScript:

var cls = document.querySelectorAll('.my-class');
for (var i = 0; i < cls.length; i++) {
    cls[i].style.display = 'none';
}

With jQuery already so popular, they should have made document.querySelector() behave just like $(). Instead, document.querySelector() only selects the first matching element which makes it only halfway useful.

Steven
  • 99
  • 1
  • 1
  • 5
    I would do a .forEach here. – Phillip Senn Nov 01 '16 at 19:06
  • 1
    Well you can always go with an easier route `document.querySelectorAll('.my-class').forEach(el => el.style.display = 'none');`. Still even if shorter, performance wise native is always better. – Alain Cruz Nov 11 '19 at 22:33
  • 1
    From user point of view, everything that happens in less than 0.1 sec, happens immediately. Therefore native even being faster, better only in case when jQuery implementation slower that 0.1 sec. And in real world application it is never the case. – Yuriy N. Jan 08 '20 at 23:21
5

Old question, but half a decade later, it’s worth revisiting. Here I am only discussing the selector aspect of jQuery.

document.querySelector[All] is supported by all current browsers, down to IE8, so compatibility is no longer an issue. I have also found no performance issues to speak of (it was supposed to be slower than document.getElementById, but my own testing suggests that it’s slightly faster).

Therefore when it comes to manipulating an element directly, it is to be preferred over jQuery.

For example:

var element=document.querySelector('h1');
element.innerHTML='Hello';

is vastly superior to:

var $element=$('h1');
$element.html('hello');

In order to do anything at all, jQuery has to run through a hundred lines of code (I once traced through code such as the above to see what jQuery was actually doing with it). This is clearly a waste of everyone’s time.

The other significant cost of jQuery is the fact that it wraps everything inside a new jQuery object. This overhead is particularly wasteful if you need to unwrap the object again or to use one of the object methods to deal with properties which are already exposed on the original element.

Where jQuery has an advantage, however, is in how it handles collections. If the requirement is to set properties of multiple elements, jQuery has a built-in each method which allows something like this:

var $elements=$('h2');  //  multiple elements
$elements.html('hello');

To do so with Vanilla JavaScript would require something like this:

var elements=document.querySelectorAll('h2');
elements.forEach(function(e) {
    e.innerHTML='Hello';
});

which some find daunting.

jQuery selectors are also slightly different, but modern browsers (excluding IE8) won’t get much benefit.

As a rule, I caution against using jQuery for new projects:

  • jQuery is an external library adding to the overhead of the project, and to your dependency on third parties.
  • jQuery function is very expensive, processing-wise.
  • jQuery imposes a methodology which needs to be learned and may compete with other aspects of your code.
  • jQuery is slow to expose new features in JavaScript.

If none of the above matters, then do what you will. However, jQuery is no longer as important to cross-platform development as it used to be, as modern JavaScript and CSS go a lot further than they used to.

This makes no mention of other features of jQuery. However, I think that they, too, need a closer look.

Manngo
  • 14,066
  • 10
  • 88
  • 110
  • 3
    Your syntax is not even correct not to mention the other wrong stuff like "slow to expose new features in Javascript" JQuery's job is not even to expose new features, it's to make it easy for you to DOM manipulate and doing simple stuff that could be like 10 lines in Javascript. Your whole comment just doesn't make any sense and has many wrong stuff in it. Consider improving it. – Boy pro Aug 30 '18 at 10:55
  • @Boypro Thanks for your comment, but it, too is full of errors. Perhaps you would like to share what it is about my answer that offends you so much. What is “not even correct”. Better still, you might like to contribute your own anwser. The question is about the cost of using jQuery when vanilla JavaScript can do so much. Consider answering it. – Manngo Aug 30 '18 at 11:22
4

as the official site says: "jQuery: The Write Less, Do More, JavaScript Library"

try to translate the following jQuery code without any library

$("p.neat").addClass("ohmy").show("slow");
dkellner
  • 8,726
  • 2
  • 49
  • 47
simon xu
  • 954
  • 2
  • 10
  • 19
  • 1
    I agree with that , however what about readability ? how can you document long lines of code with a lot of non related things happening ? How can you debug such monstrosities ? – Joel Blum Jul 16 '12 at 11:59
  • @user1032663 that's a matter of documentation-conventions. – Christoph Jul 16 '12 at 12:00
  • 1
    The alternative to jQuery (or whatever "popular" library you choose) isn't to write everything from scratch, but to use a library that suits your purpose and is well written. You may have written parts yourself, or chosen a modular library like [MyLibrary](http://www.cinsoft.net/mylib.html) to include only what you need. – RobG Jul 16 '12 at 12:56
  • 2
    The example you've chosen does not really prove your point: The question is searching for differences in the "selector" province. `addClass()` and `show()` don't really count. And as for `$('p.neat')`, you can have a look at querySelector/All. – kumarharsh Aug 18 '13 at 12:13
  • `document.querySelectorAll('p.neat').forEach(p=>p.classList.add('ohmy'));` and let CSS do the rest. Slightly longer code, but _much_ more efficient. Of course, his solution was not so available in the Legacy IE days. The “Do More” part is ironic. jQuery takes about a hundred lines of code to find something, so doing more is not always productive. – Manngo Mar 12 '20 at 03:35
2

I think the true answer is that jQuery was developed long before querySelector/querySelectorAll became available in all major browsers.

Initial release of jQuery was in 2006. In fact, even jQuery was not the first which implemented CSS selectors.

IE was the last browser to implement querySelector/querySelectorAll. Its 8th version was released in 2009.

So now, DOM elements selectors is not the strongest point of jQuery anymore. However, it still has a lot of goodies up its sleeve, like shortcuts to change element's css and html content, animations, events binding, ajax.

Vanuan
  • 31,770
  • 10
  • 98
  • 102
0
$("#id") vs document.querySelectorAll("#id")

The deal is with the $() function it makes an array and then breaks it up for you but with document.querySelectorAll() it makes an array and you have to break it up.

0

Just a comment on this, when using material design lite, jquery selector does not return the property for material design for some reason.

For:

<div class="logonfield mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
        <input class="mdl-textfield__input" type="text" id="myinputfield" required>
        <label class="mdl-textfield__label" for="myinputfield">Enter something..</label>
      </div>

This works:

document.querySelector('#myinputfield').parentNode.MaterialTextfield.change();

This does not:

$('#myinputfield').parentNode.MaterialTextfield.change();