Given jQuery's philosophy of write less, do more, I'm always surprised when I see this:
$(this).prop('checked')
… instead of this:
this.checked
Looking at the latest jQuery source, prop() provides convenience for these two gotchas:
$(elem).prop('for') is equivalent to elem.htmlFor.
$(elem).prop('class') is equivalent to elem.className.
It also normalizes tabIndex to:
0 for tabbable elements without a tab index.
-1 for non-tabbable elements.
prop() is certainly useful for setting properties for multiple elements at once, and for chaining properties on a single element.
But is there any advantage (other than idiomatic) to use prop() to set or retrieve a single property on a single element except when normalizing tabIndex – specifically when you have a reference to the element (such as this in a callback)?
.prop as a getter has no real advantage, in fact, it is less performant than directly accessing the property.
The real utility of .prop is when used as a setter.
If you read the DOC, there is 3 way of setting something with .prop.
.prop(prop, value)
The first way has no advantage for a single element(except maybe if there is compatibility issue).
In fact this.check = true; is the same as $(this).prop('checked', true), but faster.
If you have a set of element though, there is an advantage. You don't have to manually loop all elements, jQuery does it for you.
.prop({prop : value[, prop : value, ...]});
The second way is useful when you have multiple properties to change. Instead of listing every properties you want to change like that :
this.prop1=true;
this.prop2=true;
this.prop3=true;
this.prop4=true;
You can pass an object like that :
$(this).prop({
prop1 : true,
prop2 : true,
prop3 : true,
prop4 : true
});
.prop(prop, callback)
The third way is in my opinion my favorite one. Using a callback function allow you to set every element individually on a set of condition. The callback receive 2 arguments: the index and the old value.
A good example of the use of a function is to reverse the state of every checkbox:
$('checkbox').prop('checked', function(_, old){
return !old;
});
Related
I am curious about the reasoning behind some of jQuery API architecture choices.
FROM THE DOCUMENTATION
attr():
Get the value of an attribute for the first element in the set of matched elements.
val():
Get the current value of the first element in the set of matched elements.
Even if I have many objects:
$('div').val()
would only return the value of the first element.
But SETTING works differently:
val(value):
Set the value of each element in the set of matched elements.
attr(attributeName, value):
Set one or more attributes for the set of matched elements.
So my questions:
Why decide that getter operations like val() should return only the first value instead of an array of values?
(The user has to use a map operation if he wanted all of them in an array)
Why not do the same with setter operations like .val(value)?
I can see a use case (with $('div').first()) where we expect the call to val() to return the value (we know there is only one dom element in the matched set) instead of an array of length 1. But why not handle it with a concept of a "single dom element query object" (that only gets created after calls to methods like first() or eq())?
Caveat: I do not take part in the jquery development, so these thoughts may be entirely off the actual design decisions.
Anyway, imho your proposal does not seem to offer any advantage:
to process an array of data gleaned from the elements of a jquery collection the user would have to iterate over this array anyway - this can be done using jquery's each method in the first place which has the additional benefit of immediate access to the element carrying the attribute:
$("my_spiffy_selector").each( function ( idx, e ) {
$(e).attr("blarf").whatever();
});
Otoh it would complicate matters (at least the jquery code base), if the getters return type would be polymorphic or if there were different getters.
The arguably most common use case would entail at least an additional .first() call.
I got a question about Backbone, how is it possible to set all attributes of a model to empty?
unsetmodel.unset(attribute, [options])
Remove an attribute by deleting it from the internal attributes hash. Fires a "change" event unless silent is passed as an option.
But this is only meant for unsetting individual properties one by one.
Anyone an idea?
Gretz,
From Backbone site:
clearmodel.clear([options])
Removes all attributes from the model, including the id attribute.
Fires a "change" event unless silent is passed as an option.
So I would do something like:
myModel.clear();
If you want to keep the attributes, why not iterate through all of them and set them manually?
$.each(this.model.attributes, function(index, value){
// set them manually to undefined
});
I know this is an old post, but I recently came across a similar issue - mainly, that if you do unset one-by-one, you get multiple change events, with the model in an intermediate state for each one. To allow this to happen with the appropriate change events fired afterwards, you would have to unset them silently one-by-one, then manually fire change events for each one after the unsets. However, if you look at the Backbone code, you'll see that the unset method is really just a call to set, with {unset:true} in the options. So you should be able to do this instead:
model.set({ attr1: undefined, attr2: undefined, attr3: undefined }, { unset: true })
I haven't tried it in practice, but it should definitely work in theory. You would get a series of change events for each attribute, after all of the unsets have completed. This approach is going a little outside the recommended path, since it uses unexposed logic from the Backbone source, but since this particular code hasn't changed in a few years (and actually appeared to be supported as a set option before that), it should be safe to use and continue using.
There isn't a built-in method to set all properties undefined, while keeping the attributes keys. Good news is that you can easily build one yourself with a underscore one-liner:
Backbone.Model.prototype.clearValues = function(options) {
this.set(_.object(_.keys(this.attributes), []), options);
}
All models will then have a clearValues method:
var model = new Model({
id:1,
foo:'foo',
bar:'bar'
});
model.clearValues();
console.log(model.toJSON()); //-> {id: undefined, foo: undefined, bar: undefined}
Typically in a case where jQuery fails to find matching elements using its selector the default value is never an exception but rather a lack of action.
My understanding is this is done by design to ensure that the lack of an element does not result in an error condition. However I have found that jQuery.val() does not exhibit this same behaviour. If a selected element does not exist jQuery.val() will return undefined rather than an empty string (which in keeping with the remainder of the framework I would have expected).
So why is jQuery.val() an exception to the rest of the framework and what would be the best way to change this behaviour?
The key here is chainability of jQuery methods. A jQuery method doesn't return undefined when a selector doesn't match anything because it would break the chain. Consider:
$( '#doesntExist' ).addClass( 'foo' ).fadeIn();
If the selector that doesn't match anything would return undefined it would break the chain since undefined doesn't have any methods of its own. Now every method is executed (and do nothing) whether or not the element is found.
.val() on the other hand is different since it's purpose is to return the value of an element. It doesn't chain at all unlike many other jQuery methods, because a method has to return a jQuery object for it to be able to chain. For example, $( '#foo' ).val().addClass( 'bar' ) does not work regardless of whether the element #foo exists or not.
In this light having .val() return an empty string would not make it any more in line with chainable methods, since no other method returns an empty string when the element isn't found either, and returning undefined does not break chainability since the method doesn't chain in the first place.
(By the way, the same is true for every method that returns a value, like .css() and .data().)
Usually it's more useful to know whether the value of an element is empty or whether the element doesn't exist at all, but if you prefer to always get a string back you can add a method of your own:
$.fn.stringVal = function() {
return( this.val() || '' );
};
If I use .val() to get the value of an item, I expect that item to exist. If it does not, I need to know that. Returning undefined in this case seems the best way.
As pointed out by #Ricardo, .val() performs differently in other cases as well, depending on the input.
jQuery.val() returns the value of an input (or the selected option of a "select")
Before jQuery I would have used something like the following code to set a class on an element:
document.getElementById("MyElementID").className = 'MyClassName';
This has the intended behaviour of replacing anything that was there before my new class name MyClassName
In jQuery the equivalent seems to be:
$('#MyElementID').attr('class', 'MyClassName')
But, we also have the in-built class functions, addClass(), removeClass() and toggleClass(). So to achieve the same effect, I would say:
$('#MyElementID').removeClass().addClass('MyClassName');
Obviously, the first jQuery example is more concise and requires just one function call (to attr, instead of addClass() and removeClass())
Can we also work on the assumption that I can't use prop as its an earlier (and currently unchangeable) version that I'm working with.
So what would you say is the preferred approach? Does the second sample give me anything over and above the first?
Many thanks in advance.
The addClass(), removeClass() and has() methods of jQuery use the className property of the element. The big advantage they offer is that they allow you to add or remove a class without affecting the other class names set.
The attr() method uses something like this:
document.getElementById("MyElementID")[name] = value;
For cases that you only need one class name, the attribute method will be probably faster. I personally however find the addClass method more elegant and easier to adapt if you need to add more class names in the future.
I think you should check the performance of all these functions. personally i think prop method is fastest ( introduced in v1.6)
see here the performance jQuery().attr vs jQuery().data vs jQuery().prop
http://jsperf.com/jquery-data-vs-jqueryselection-data/8
The difference is in this guy:
<div class="a_class another_class a_third_class">
attr('class','no_other_classes_now') <-- will replace everything in the class attribute even if there are multiple space separated classes. It's probably the lightest-weight of JQ methods for doing this since it's just using DOM methods that already worked properly across browsers for over a decade now.
.removeClass('a_third_class') will remove a class, leaving other classes intact.
.addClass('a_fourth_class') will add a space-separated class without replacing other classes.
^^ These two would have to do more work for a simple class overwrite than attr since they need to do find/replace type operations.
Prop is for changing properties of DOM elements that don't have HTML representatives like the window object (not likely to be useful very often due to other convenience methods) or that would be confusing because different attribute strings can mean different things. Like simply having the attribute "checked" without an equal sign or value being equivalent to checked="checked" or checked="true" as far as the boolean (true/false only) JS property is concerned in certain flavors of HTML. With prop you'll get the javascript property and not necessarily whatever is considered to be between the quotes of the actual HTML element.
When you're not in that type of situation, I would stick with the attr method. The whole point of prop seems to be to take work from the old attr method so I'd be surprised if it was faster in most cases. More importantly it's common and easy to read.
How come the equation in the title is false? How do check if two jQuery selectors point to the same DOM object?
You are comparing two distinct jQuery objects because you call $() twice (once for each side of the equation), and as MooGoo explains jQuery creates new wrapper objects for each time you call it. That's why the comparison ends up returning false.
You can extract a DOM object from each jQuery object by either using get() or array dereferencing, then compare these elements. The following both return true because both identical selectors match the same body DOM element:
$('body').get(0) == $('body').get(0)
$('body')[0] == $('body')[0]
If you want to test against a jQuery selector, use is(). Note that, unless your selectors are identical, the selectors you use may not necessarily match the same DOM elements (it's still better to use the above). This also returns true:
$('body').is('body')
Because jQuery creates a new wrapper object for each $ call, and in Javascript all objects are distinct, even if they have the exact same properties/methods.
On the other hand, document.body == document.body would evaluate to true.
Use $.is()
http://api.jquery.com/is/
Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments...
Unlike other filtering methods, .is() does not create a new jQuery object. Instead, it allows you to test the contents of a jQuery object without modification. This is often useful inside callbacks, such as event handlers...