Wednesday, 4 May 2011

Big compatibility break in jQuery 1.6

jQuery 1.6 is out, and unusually for jQuery, there's a pretty big API compatibility break with regard to the attr function. Details in the release notes, but basically, attr really is just for attributes now, whereas before it sort of conflated attributes and properties. There's a new prop function for interacting with properties.

Update 2011/05/11: jQuery 1.6.1 changes attr again to make it more backward-compatible for older code, details in the jQuery 1.6.1 blog post.

It's a seemingly-small change, but I've seen a lot of code that will be affected, especially around the "checked" property. If you've been checking whether a checkbox is checked using code along the lines of

// In an event handler, etc.:
if ($(this).attr('checked')) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo.attr('checked')) {
// It's checked
}
...you'll need to update your code. Personally, I prefer going straight to the DOM element (that's what the jQuery team recommends as well):
// In an event handler, etc.:
if (this.checked)) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo[0] && foo[0].checked) {
// It's checked
}
...and the above has the advantage of working regardless of what version of jQuery you're using. But you could use prop instead if you like:
// In an event handler, etc.:
if ($(this).prop('checked')) {
// It's checked
}
// Or if you already have a jQuery object for the checkbox:
if (foo.prop('checked')) {
// It's checked
}
(There's also foo.is(':checked'), but that's awfully round-about.)

The big properties I see people stumbling over are "checked" (hence the above) and "value" (but hopefully you were already using val anyway).

7 comments:

Petr 'PePa' Pavel said...

What would you recommend as a backward compatible method for enabling and disabling an input field?

Thanks
Petr

T.J. Crowder said...

@PePa: For checking state, probably the same as for `checked` above. For changing state, I use a tiny plug-in I dashed off that sets the `disabled` property on the matched elements that have it; example.

crescentfresh said...

A "fix" is coming already: http://news.ycombinator.com/item?id=2512473

In getter-style invocation of boolean properties, attr() will - as of 1.6.1 - return the attribute name in lowercase if the property is true.

So your breaking example of "if ($el.attr('checked'))" will work once again by virtue of the string "checked" evaluating as truthy in JavaScript. What a mess.

Petr 'PePa' Pavel said...

Thanks for your plug-in T.J.
Here's perhaps something shorter, inspired by a code from here:
http://news.ycombinator.com/item?id=2511733

$.fn.disable = function(bool) {
  if (bool) {
    this.attr('disabled', 'disabled');
  } else {
    this.removeAttr('disabled');
  }
}

T.J. Crowder said...

@crescentfresh: OMG

@PePa: Nice! But I prefer to go straight to the property rather than further indirection.

RobG said...

It is amazing how often you see code like: $(this).attr('...') to access a DOM property, which is increadibly inefficient. At least the jQuery team gets a tick for finally addressing some fundamental issues with attr.

T.J. Crowder said...

@RobG: Yeah. I was glad to see the docs for prop actually show an example of testing properties directly (if (elem.checked)). I've pointed a couple of people at this article but always intended to do my own version of it. This may be a good time, as prop offers very little on top of a standard loop and direct access. (Unlike attributes, which are a mess cross-browser, where attr really helps you out.)