jQuery 1.6 and .attr()

jQuery 1.6 and 1.6.1 are out the door. Congrats to the team and everyone that was involved with the release!

A relatively controversial change in 1.6 was regarding how attributes and DOM object properties were handled. In 1.6 we wanted to take the major step of completely separating the two, allowing us to create an .attr() method that wasn’t quite so mealy-mouthed with regards to how attributes were handled. We did this in 1.6 because we felt that it was a substantial change (we only do major changes in the 1.x major releases of jQuery) and had the possibility of affecting people.

We did a considerable amount of testing on the code and we were quite confident that the amount of problems that people would encounter, while upgrading, would be quite minimal. The biggest pain points, we surmised, would be regarding how boolean attributes were handled (attributes like “disabled” or “selected”). However most of this would be mitigated and would likely have worked fine for users that consistently used .attr() to access and update their attributes.

After making the changes, and publishing 1.6, there were enough complaints that we had changed the API to cause us to reconsider and return .attr() to its sometimes-attribute, sometimes-property, state.

jQuery is in an incredibly tricky position now (and has been for some time). We very rarely add features to the library, for fear of bloat and added API maintenance overhead, and are rarely able to make any sort of API change, for fear of preventing people from upgrading.

Thankfully even though we’ve reverted some of the changes in 1.6, we’ve done it in a way that still maintains the performance gains that we achieved with the 1.6 release.

I wanted to try and summarize the .attr() method, to explain how it currently works in 1.6.1, but ended up writing up a sample function instead (note that this is a bit of an over-simplification, please read the code for more details):

function attr( elem, name, value ) {
// Are we setting a value?
if ( value !== undefined ) {
// Make sure the element has the ability to set an attribute
if ( typeof elem.setAttribute !== "undefined" ) {
// If the user is setting the value to false
if ( value === false ) {
// Completely remove the attribute
elem.removeAttribute( name );
// Otherwise set the attribute value
} else {
// If the user is setting the value to true,
// Set it equal to the name of the attribute
// (handles boolean attributes nicely)
elem.setAttribute( name, value === true ? name : value );
}
// If it doesn't, then we're likely dealing with window or document
// (or some other object entirely)
} else {
elem[ name ] = value;
}
// Otherwise we're getting an attribute value
// Check to see if the appropriate method exists
// Also don't use getAttribute if a boolean property exists
} else if ( typeof elem.getAttribute !== "undefined" &&
typeof elem[ name ] !== "boolean" ) {
return elem.getAttribute( name );
// If no getAttribute method is present, or if we
// wish to access the boolean property instead of the
// attribute, then we fallback to the DOM object property
} else {
return elem[ name ];
}
}

There’s a very good chance that your code, written targeting 1.5.2, will continue to work just fine in 1.6.1 using this particular technique.

However this point now begs the question: Why does .prop() exist?

In short, for two reasons:

There are legitimate use cases for interacting with some DOM properties (such as nodeName, selectedIndex, or defaultValue) and we want to provide a simple solution for accessing, and mutating, them.

Accessing properties through the .attr() method will be slightly slower than accessing them directly through .prop() (as .attr() calls .prop() internally in order to handle all property-related mutation).

In jQuery 1.5.2, and older, in order to access a DOM property you would have to do something like this:

var elem = $("#foo")[0];
if ( elem ) {
index = elem.selectedIndex;
}

In 1.6+ you can just do:

index = $("#foo").prop("selectedIndex");

Summary: There’s a good chance that your code won’t be affected at all with the changes that’ve happened, especially so with the changes in 1.6.1, and jQuery now has a convenience method for handling DOM properties in a simpler, and slightly faster, manner.

Tangentially this reminds me of a common question that I hear: What will be in jQuery 2.0? I have no idea what will be in that release, should it ever arrive, but I do know what won’t be in it: A massive API change of any sort. Even when we make, relatively minor, API changes like in the 1.6 release the amount of negative feedback that we get is monumental. If we’ve learned anything after doing 31 releases of jQuery it’s that people like having stability in their API and will cherish that over everything else.

I do want to thank the community though for being so vocal and working to communicate with the team so actively. Without the community’s communication and support it’s doubtful that the team would be able to continue operating.

I would like to take this opportunity to encourage everyone to get involved with the development of jQuery. We hold active discussions every day in IRC and hold public meetings once a week. We also post weekly status updates if you wish to follow along. Right now we’re working on the 1.7 release of the library and are actively encouraging contributions and feedback. If you want to help ensure the quality and stability of the next release of jQuery, the best way to do so is to get involved. Hope to see you around the bug tracker.

Big misstake to revert in 1.6.1. People are to lazy to read changelog and do search and replace but they have to upgrade on the same freaking day of the release! Damn curled kids. Grow up or I’ll bet you all back to the times before php, with nasty perl code bitchslapping you in your cgi face. Ill tell you upgrading something in those times where not a matter of drag and drop to replace some file kids!

Well, jQuery 1.6 broke some stuff also in my case, but I think it’s to the devs (me) to fix that, not you. You provide great stuff, and I’m only thankful for what you and the jQuery team do.
Everybody should follow the API and try updating and fixing as much as possible (sharing what bugs have been spotted and their patches, obviously).
1.7 will probably not be that different from 1.6, but you shouldn’t fear the end users. That’s what release versioning is for!

Personally, I’m for inclusion of stuff in the jQuery repository (some kind of way Symfony does). Not in core, but in some “officially supported” repo.
With that, a better script loader would be really great! Something that removes the evil document.write forever :D

I haven’t had a chance to really look at the changes yet but I will say this. If this is the best thing for the performance and usability of the library, then I am all for it. If not, and it’s just due to the negativity and lack of ambition on the parts of those who don’t want to really learn how to use JavaScript and jQuery, then I think that’s a loss for the rest of us.

I think what some people still don’t understand is that jQuery is a tool to help them enhance their websites, NOT to hold their hands and magically build it for them. Plus, it’s free people. So until you contribute to the community or pay for it, maybe you should stop whining, roll up your sleeves and do your homework to build yourself a better website.

Great work and great post John. Although I’d agree with the people who say that it’s up to the devs to make their changes to their code according to your APIs. That said, I wouldn’t make major changes except on the 2.0, 3.0, etc. In the meantime, you could deprecate certain code and let the devs know about it well in advance.

Thanks for posting this John. I’ve been curious of your perspective on the fallout of 1.6.

Why did you choose to add API compatibility in 1.6.1 instead of providing a compatibility plugin as you’ve done for 1.4 and other releases? I’m sure this was considered, so I’m curious what the rationale was for adding backwards compatibility in 1.6.1.

For the record, I agree that jQuery’s backwards compatibility is critical to jQuery’s continued success. The proliferation of plugins create a complex, potentially unresolvable dependency graph if there are incompatible jQuery versions.

Personally i am glad you reverted. The attr change was a little too big. Had you swapped the attr and prop functions (not names) I would agree. However the potential for broken code was a little too great in 1.6. You need to bear in mind non greenfield apps wishing to take advantage of upgrades without changes to code base. Isnt this the sum totalselling point of a library?

I believe you are starting to suffer from Microsoft syndrome. Trying to be everything to everyone is not an easy task.

The users need to know what they are upgrading before they upgrade. Making beneficial changes is a must in any development environment. It’s up to us to upgrade with caution and understand what we are getting into. Great work on making jQuery better. Looking forward to whatever else is in store.

I’ve been critical on Hacker News about the messages coming from the jQuery team about both 1.6 and 1.6.1, so thank you for taking the time to explain your reasoning. However… some questions:

1) Why are you recommending attr() over prop(), and hence apparently attributes over properties? Properties are the way to go, as you well know.

2) Apart from the performance boost, how is the current situation (two methods rather than one with no clear rationale for which one should be used) better than the previous situation?

3) The consequences (lots of sites breaking when trying to upgrade) of the attr/prop change in 1.6 were obvious but conceptually it was the right way to go. It now looks like you’ve bottled it. Have you been pressured by users to revert the 1.6 change?

Michael: No, “for” is not a valid attribute for an element and as a result, adding a “for” attribute will not be reflected as a property called “for” in most browsers. Furthermore, “for” is a reserved word in JavaScript meaning that it cannot be used as an object property name. attr() uses properties in most cases prior to 1.6, hence your problem. Since the attribute is not valid, you shouldn’t really expect any particular behaviour anyway.

While I agree with most of the commenters that say we cannot halt progress for the sake of compatibility, I was one of those opposed to the API change when 1.6 hit. I’m happy to update my code (this is not a difficult refactor) but I think some of the distaste for the change came from the fact that the incompatibility was so poorly communicated. If there had been a better communication of which code would be at risk and what the update path was then you would have seen much less opposition.

Tim, your first response to Michael doesn’t make sense to me. Where is it written that attr only works with valid attribute names? That’s not a requirement of the DOM — getAttribute('blerg') will happily retrieve whatever value you’ve defined for blerg.

Any thought to releasing building “jQueryDeuce”? Take the best of what is in jQuery, do not abandon jQuery, continue to upgrade and improve jQuery, but redo the whole thing the way you would if you started from scratch and release it as a separate library?

Andrew: Where is it written *exactly* what attr() is supposed to do? In particular in the case of custom attributes? A custom attribute is accessible via getAttribute() but not via a property in most browsers, while attr() (prior to 1.6) used an undocumented combination of properties and attributes, so it’s unclear what to expect.

Why would someone, upgrading from a 1.x version of a library to a 2.x version, expect anything less than at least a few major changes? This concern hasn’t stood in the way of, e.g. Rails developers, with Rails 3 containing numerous breaking changes for the sake of a better library.

My initial reaction is that you shouldn’t pander to all the lazy users, no matter how many or how vocal. The “purity” of he solution should be paramount. It now seems that there are a lot of people out there who would agree with me. I think 1.6.1 was a small step backwards. You can’t (and shouldn’t) please all (or most) of the people all (or most) of the time.

I personally feel that “caveat emptor” is the correct approach when upgrading any piece of OSS. At least read the release notes, people. For 1.6, right at the top, was a section called “Breaking Changes.” How could that have been more clear?

In fairness, it may be better practice to deprecate features several releases prior to their change/removal. Either way, I’m incredibly thankful and indebted to the jQuery team for all their hard work and dedication to the project. jQuery continues to be a killer library because of you folks!

If someone is implementing the newer version of jQuery it should be expected that there would be some glitches and changes.

I personally don’t update jQuery to the latest version on every production site, I only do that when doing maintenance or updating the code. In those situations I’d not expect everything to work straight off.