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 ];

Ironically this isn’t much shorter than the actual .attr() implementation, I recommend that you check it out.

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:

  1. 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.
  2. 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.

Posted: May 13th, 2011

Subscribe for email updates

41 Comments (Show Comments)

Comments are closed.
Comments are automatically turned off two weeks after the original post. If you have a question concerning the content of this post, please feel free to contact me.

Secrets of the JavaScript Ninja

Secrets of the JS Ninja

Secret techniques of top JavaScript programmers. Published by Manning.

John Resig Twitter Updates

@jeresig / Mastodon

Infrequent, short, updates and links.