50

Is there any difference between

$('input.current_title', '#storePreferences').prop('disabled', false);

and

$('#storePreferences input.current_title').prop('disabled', false);

?

Dims
  • 47,675
  • 117
  • 331
  • 600

3 Answers3

67

There IS a difference, and it is NOT subtle as others believe.

EDIT: Layman's example of each:

  • Call all the blue houses in town (context), if Jane is there, tip off her hat.
  • Call all the buildings in town (no context yet). IF it is a blue house (add context) and Jane is there, tip off her hat.

Let's break down what it selects.

First we have: Context selector http://api.jquery.com/jQuery/#jQuery-selector-context

$('input.current_title', '#storePreferences').prop('disabled', false);

This says: use a selector in context. http://api.jquery.com/jQuery/#jQuery-selector-context

While this form MIGHT work, it should really be:

$('input.current_title', $('#storePreferences')).prop('disabled', false);

OR

var myContext = $('#storePreferences');
$('input.current_title', myContext).prop('disabled', false);

This meets the requirement for a context selector being met: "A DOM Element, Document, or jQuery to use as context".

This says: using the context, find inside that the selector. An equivalent would be:

$('#storePreferences').find('input.current_title').prop('disabled', false);

Which is what happens internally. Find '#storePreferences' and in that find all the 'input.current_title' matching elements.


Then we have: Descendant Selector

$('#storePreferences input.current_title').prop('disabled', false);

This is a Descendant Selector (“ancestor descendant”) http://api.jquery.com/descendant-selector/ which says: find all the input.current_title elements inside the #storePreferences element. THIS IS WHERE IT GETS TRICKY! - that is EXACTLY what it does -

finds ALL the input.current_title (anywhere), then finds those INSIDE the #storePreferences element.

Thus, we run into jQuerys' Sizzle right to left selector - so it initially finds MORE(potentially) than it needs which could be a performance hit/issue.

Thus the form of:

$('#storePreferences').find('input.current_title').prop('disabled', false);

would perform better than the Descendant version most likely.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
  • When the second parameter to `jQuery` is a string, it performs the context lookup, so I'd guess that there's simply a bug with the documentation that needs to be addressed. – zzzzBov May 07 '13 at 16:13
  • @zzzzBov - likely true about the documentation but strictly speaking, it is what we have to go by at this juncture thus why I addressed that in the manner I have. – Mark Schultheiss May 07 '13 at 16:16
  • 3
    implementation always trumps documentation. – zzzzBov May 07 '13 at 16:18
  • 5
    I prefer to code to documentation and report implementation bugs - if they turn out to be _documentation_ bugs then fine, but if the docs have been constructed from design and the product constructed with TDD then in theory things should match up. Coding to interface is good – Lightness Races in Orbit May 08 '13 at 07:10
  • 1
    Well, nowadays it's almost impossible that JQuery will change it's implementation and broke code that pass selector string as second argument. So in practice there is probably no harm in using it that way, except that you don't abide to documentation as you should and that just feels _wrong_. – Mariusz Pawelski Aug 06 '18 at 11:46
  • I read this and still could not understood what was the difference. – Shashank Bhatt Feb 06 '21 at 13:14
34

Is there any difference between $('input.current_title', '#storePreferences').prop('disabled', false); and $('#storePreferences input.current_title').prop('disabled', false);?

Yes, but it's subtle

The difference is in how the elements are selected.

$('input.current_title', '#storePreferences');

is equivalent to1:

$('#storePreferences').find('input.current_title');

but is not equivalent to:

$('#storePreferences input.current_title');

even though the same elements will be affected.

The reason they're not the same is that using find allows for the context to be returned to #storePreferences when end is called.

1: lines 194-202 in the jQuery v1.9.1 source
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
    return ( context || rootjQuery ).find( selector );

// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
    return this.constructor( context ).find( selector );
}

in the context of your question, the same elements will be modified, so there is no difference in functionality, but it's important to be aware of the broader implications of the selectors you use.

Community
  • 1
  • 1
zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • 1
    Can you provide a simple case example where this syntaxes effect differently please? – Dims May 07 '13 at 15:18
  • 1
    This is not the entire answer to this question although he changed the selectors which DOES change the question - significantly in this case. – Mark Schultheiss May 07 '13 at 15:47
  • @MarkSchultheiss, the change to the selectors was trivial. Performance was not, and should not, be part of this discussion as it's truly premature optimization. There is so much asynchronous behavior in JavaScript that performance is rarely an issue worth focusing on. – zzzzBov May 07 '13 at 16:10
  • 1
    But performance in this case IS an significant part of the difference and has NOTHING to do with optimization but does have to do with what happens internally in jQuery which is "is there a difference" - and the change from finding an element by id and by class is, in my opinion not trivial. – Mark Schultheiss May 07 '13 at 16:14
0

In your example I believe that there is little difference.

It comes into better use when you start selecting multiple elements in a specific DOM element.

// Get the div in the body with the id of storePreferences
var sp = $('body div#storePreferences');


// jQquery will only look for **input.current_title** **input.name** **input.age** in side **sp** div in the DOM.
// Faster
$('input.current_title', sp).prop('disabled', false);
$('input.name', sp).prop('disabled', false);
$('input.age', sp).prop('disabled', false);




// jQquery will look for **input.current_title** in entire DOM
// Slower
$('#storePreferences input.current_title').prop('disabled', false);
Hello-World
  • 9,277
  • 23
  • 88
  • 154
  • Since it is really what this question is about your comment: "// Get the div in the body with the id of storePreferences `var sp = $('body div#storePreferences');` " This is NOT strictly true, and IMHO a bad selector, having an ID, it should always be used and ONLY that used as fastest especially in older browsers. An ID is required to be unique in the document per specification. What your selector really does is 1. Select the `#storePreference` element by ID. THEN find all those `#storePreference` elements in a `div`, THEN find that in the `body` – Mark Schultheiss Feb 06 '21 at 22:19
  • This is due to the right to left of the selector use and progression in the Sizzle engine in use. Going through all the div elements then the body after you already have an element by ID is not only inefficient but IMHO more confusing than simply `var sp = $('#storePreference');` – Mark Schultheiss Feb 06 '21 at 22:19
  • The fastest, given your last example `$('#storePreferences input.current_title').prop('disabled', false);` is actually to use `$('#storePreferences').find('input.current_title').prop('disabled', false);` – Mark Schultheiss Feb 06 '21 at 22:22
  • Note, you are correct thus that `$('#storePreferences input.current_title').prop('disabled', false);` is slower than the context based selections above that. – Mark Schultheiss Feb 07 '21 at 16:24