If you are passing data to a DOM element from the server, you should set the data on the element:
<a id="foo" data-foo="bar" href="#">foo!</a>
The data can then be accessed using .data()
in jQuery:
console.log( $('#foo').data('foo') );
//outputs "bar"
However when you store data on a DOM node in jQuery using data, the variables are stored on the node object. This is to accommodate complex objects and references as storing the data on the node element as an attribute will only accommodate string values.
Continuing my example from above:
$('#foo').data('foo', 'baz');
console.log( $('#foo').attr('data-foo') );
//outputs "bar" as the attribute was never changed
console.log( $('#foo').data('foo') );
//outputs "baz" as the value has been updated on the object
Also, the naming convention for data attributes has a bit of a hidden "gotcha":
HTML:
<a id="bar" data-foo-bar-baz="fizz-buzz" href="#">fizz buzz!</a>
JS:
console.log( $('#bar').data('fooBarBaz') );
//outputs "fizz-buzz" as hyphens are automatically camelCase'd
The hyphenated key will still work:
HTML:
<a id="bar" data-foo-bar-baz="fizz-buzz" href="#">fizz buzz!</a>
JS:
console.log( $('#bar').data('foo-bar-baz') );
//still outputs "fizz-buzz"
However the object returned by .data()
will not have the hyphenated key set:
$('#bar').data().fooBarBaz; //works
$('#bar').data()['fooBarBaz']; //works
$('#bar').data()['foo-bar-baz']; //does not work
It's for this reason I suggest avoiding the hyphenated key in javascript.
For HTML, keep using the hyphenated form. HTML attributes are supposed to get ASCII-lowercased automatically, so <div data-foobar></div>
, <DIV DATA-FOOBAR></DIV>
, and <dIv DaTa-FoObAr></DiV>
are supposed to be treated as identical, but for the best compatibility the lower case form should be preferred.
The .data()
method will also perform some basic auto-casting if the value matches a recognized pattern:
HTML:
<a id="foo"
href="#"
data-str="bar"
data-bool="true"
data-num="15"
data-json='{"fizz":["buzz"]}'>foo!</a>
JS:
$('#foo').data('str'); //`"bar"`
$('#foo').data('bool'); //`true`
$('#foo').data('num'); //`15`
$('#foo').data('json'); //`{fizz:['buzz']}`
This auto-casting ability is very convenient for instantiating widgets & plugins:
$('.widget').each(function () {
$(this).widget($(this).data());
//-or-
$(this).widget($(this).data('widget'));
});
If you absolutely must have the original value as a string, then you'll need to use .attr()
:
HTML:
<a id="foo" href="#" data-color="ABC123"></a>
<a id="bar" href="#" data-color="654321"></a>
JS:
$('#foo').data('color').length; //6
$('#bar').data('color').length; //undefined, length isn't a property of numbers
$('#foo').attr('data-color').length; //6
$('#bar').attr('data-color').length; //6
This was a contrived example. For storing color values, I used to use numeric hex notation (i.e. 0xABC123), but it's worth noting that hex was parsed incorrectly in jQuery versions before 1.7.2, and is no longer parsed into a Number
as of jQuery 1.8 rc 1.
jQuery 1.8 rc 1 changed the behavior of auto-casting. Before, any format that was a valid representation of a Number
would be cast to Number
. Now, values that are numeric are only auto-cast if their representation stays the same. This is best illustrated with an example.
HTML:
<a id="foo"
href="#"
data-int="1000"
data-decimal="1000.00"
data-scientific="1e3"
data-hex="0x03e8">foo!</a>
JS:
// pre 1.8 post 1.8
$('#foo').data('int'); // 1000 1000
$('#foo').data('decimal'); // 1000 "1000.00"
$('#foo').data('scientific'); // 1000 "1e3"
$('#foo').data('hex'); // 1000 "0x03e8"
If you plan on using alternative numeric syntaxes to access numeric values, be sure to cast the value to a Number
first, such as with a unary +
operator.
JS (cont.):
+$('#foo').data('hex'); // 1000
Best Answer
Update 1 November 2012
My original answer applies specifically to jQuery 1.6. My advice remains the same but jQuery 1.6.1 changed things slightly: in the face of the predicted pile of broken websites, the jQuery team reverted
attr()
to something close to (but not exactly the same as) its old behaviour for Boolean attributes. John Resig also blogged about it. I can see the difficulty they were in but still disagree with his recommendation to preferattr()
.Original answer
If you've only ever used jQuery and not the DOM directly, this could be a confusing change, although it is definitely an improvement conceptually. Not so good for the bazillions of sites using jQuery that will break as a result of this change though.
I'll summarize the main issues:
prop()
rather thanattr()
.prop()
does whatattr()
used to do. Replacing calls toattr()
withprop()
in your code will generally work.checked
property is a Boolean, thestyle
property is an object with individual properties for each style, thesize
property is a number.value
andchecked
: for these attributes, the property always represents the current state while the attribute (except in old versions of IE) corresponds to the default value/checkedness of the input (reflected in thedefaultValue
/defaultChecked
property).If you're a jQuery developer and are confused by this whole business about properties and attributes, you need to take a step back and learn a little about it, since jQuery is no longer trying so hard to shield you from this stuff. For the authoritative but somewhat dry word on the subject, there's the specs: DOM4, HTML DOM, DOM Level 2, DOM Level 3. Mozilla's DOM documentation is valid for most modern browsers and is easier to read than the specs, so you may find their DOM reference helpful. There's a section on element properties.
As an example of how properties are simpler to deal with than attributes, consider a checkbox that is initially checked. Here are two possible pieces of valid HTML to do this:
So, how do you find out if the checkbox is checked with jQuery? Look on Stack Overflow and you'll commonly find the following suggestions:
if ( $("#cb").attr("checked") === true ) {...}
if ( $("#cb").attr("checked") == "checked" ) {...}
if ( $("#cb").is(":checked") ) {...}
This is actually the simplest thing in the world to do with the
checked
Boolean property, which has existed and worked flawlessly in every major scriptable browser since 1995:if (document.getElementById("cb").checked) {...}
The property also makes checking or unchecking the checkbox trivial:
document.getElementById("cb").checked = false
In jQuery 1.6, this unambiguously becomes
$("#cb").prop("checked", false)
The idea of using the
checked
attribute for scripting a checkbox is unhelpful and unnecessary. The property is what you need.checked
attribute