What is the difference between .data('sth') and .attr('data-sth')? [duplicate] - javascript

What is the difference in usage between $.data and $.attr when using data-someAttribute?
My understanding is that $.data is stored within jQuery's $.cache, not the DOM. Therefore, if I want to use $.cache for data storage, I should use $.data. If I want to add HTML5 data-attributes, I should use $.attr("data-attribute", "myCoolValue").

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

The main difference between the two is where it is stored and how it is accessed.
$.fn.attr stores the information directly on the element in attributes which are publicly visible upon inspection, and also which are available from the element's native API.
$.fn.data stores the information in a ridiculously obscure place. It is located in a closed over local variable called data_user which is an instance of a locally defined function Data. This variable is not accessible from outside of jQuery directly.
Data set with attr()
accessible from $(element).attr('data-name')
accessible from element.getAttribute('data-name'),
if the value was in the form of data-name also accessible from $(element).data(name) and element.dataset['name'] and element.dataset.name
visible on the element upon inspection
cannot be objects
Data set with .data()
accessible only from .data(name)
not accessible from .attr() or anywhere else
not publicly visible on the element upon inspection
can be objects

You can use data-* attribute to embed custom data. The data-* attributes gives us the ability to embed custom data attributes on all HTML elements.
jQuery .data() method allows you to get/set data of any type to DOM elements in a way that is safe from circular references and therefore from memory leaks.
jQuery .attr() method get/set attribute value for only the first element in the matched set.
Example:
<span id="test" title="foo" data-kind="primary">foo</span>
$("#test").attr("title");
$("#test").attr("data-kind");
$("#test").data("kind");
$("#test").data("value", "bar");

Related

How to work with custom Custom data-* attributes [duplicate]

I am updating a data attribute by jQuery, Like:
jQuery('div').data('hidden', 'true');
alert(jQuery('div').data('hidden'));
Data attribute value got changed and returned new value which is true but DOM is still showing the old value which is false.
When you use .data() to update a data value, it is updating internal object managed by jQuery, so it will not be updated in the data-* attribute
I was beating around the bush so badly :( and able to solve the problem. Seams like we can't do achieve this using the jquery data method if the html is dynamic and the data attribute changed later after accessing the first time.
According to jQuery.data()
The data- attributes are pulled in the first time the data property is
accessed and then are no longer accessed or mutated (all data values
are then stored internally in jQuery).
So what I did is, changed it to attr method which won't give you the parsed value for integer, so you have to use '+' operand to convert like:
+ myElement.attr('data-index');
Note: You have to be careful, it will convert the result to NaN if there is any string in the data attr. BTW it's your choice of implementation of the code.

Is there an advantage in how I store my data using jQuery?

I know understand more about how jQuery stores data.
Is there any advantage to doing one or the other of these:
$('#editCity').data('href', "xx");
var a = $('#editCity').data('href');
or
$('#editCity').attr('data-href', "xx");
var a = $('#editCity').attr('data-href');
One more related question.
If I have this:
var modal = { x: 'xx', y: 'yy' };
Can I also store this using .data( .. ) ?
Storing property values directly on DOM elements is risky because of possible memory leaks. If you're using jQuery anyway, the .data() mechanism is a wonderful and safe way to keep track of per-element information. It allows for storage of arbitrary JavaScript data structures too, not just strings.
edit — when your HTML markup contains data-foo attributes, those are implicitly read when the keys are accessed. That is, if your HTML looks like this:
<div id="div1" data-purpose="container">
Then in jQuery:
alert( $('#div1').data('purpose') ); // alerts "container"
Furthermore, jQuery will also do some "smart" analysis of the attribute values. If a value looks like a number, jQuery will return a number, not a string. If it looks like JSON, it de-serializes the JSON for you.
edit — here's a good trick: in the Chrome developer console (the "Console" view), you can type JavaScript expressions and have them evaluated. The evaluation is always done in the context of the page you're working on. If you select an element from the "Elements" view, then in the console the JavaScript symbol $0 will refer to that selected DOM element. Thus you can always inspect the "data" map for an element by going to the console and typing something like:
$($0).data("something");
The .data() function, if called with no parameters, returns all the key/value pairs:
$($0).data();
The most interesting point about the data function is that you can add any kind of object, for example your modal. And jQuery, as stated in the documentation, take care of avoiding memory leaks when the DOM changes :
The jQuery.data() method allows us to attach data of any type to DOM
elements in a way that is safe from circular references and therefore
free from memory leaks.
For strings, memory leaks aren't possible but the main difference is that the first solution is cleaner (more coherent if you might store other data than strings in other parts of your application), clearer (the intent is evident), and doesn't force CSS calculation (DOM isn't changed).
They both have advantages... That said, 99% of the time you should be using .data('whatever', value)
Advantages of using .data('whatever', value):
less apt to cause memory leaks because it's not using the DOM.
Slightly faster to pull data from memory than from the DOM.
Can put any type of object in it without serializing it to JSON first.
Advantages of using .attr('data-whatever', value):
compatible with .data('whatever')
allows you to select the element by the value: $('[data-whatever=foo]')
You can put any object in it, but it will need to serialize if it's a complex type.

Can a DOM element have an attribute whose value is an arbitrary object (not a string)?

I'd like to do something like this:
var elem = document.createElement("input");
elem.setAttribute("my-attribute", myObject);
document.getElementById("parent").appendChild(elem);
Later I will need to fetch myObject when performing some actions on this (and similar) element(s).
Note: I need this as an attribute (and not, for example, as a member of the element object, as in elem.myAttribute = myObject), as for some elements the value is a string which is hard-coded into the HTML of the page. What I need is the ability to set this attribute programmatically for other elements, and to use values which are not always plain strings.
I tried this and it worked in my browser (Firefox 14), but I need to know if this works cross-browser, and also if I'll be able to fetch the values of such attributes using jQuery if I decide to use jQuery in my page later on.
No - attributes by definition store string values. The obvious approach is to store the object as a property but you say that's not suitable.
Either:
1) Use jQuery's data API (since it does not literally log the data on the element, so you can store whatever you want, not only strings)
2) Stringify the object and append that to the element as an HTML5 data attribute.
var elem = document.querySelector('p'),
obj = {foo: 'bar'};
elem.setAttribute('data-myObj', JSON.stringify(obj));
/* ...then, later... */
var data = JSON.parse(elem.getAttribute('data-myObj'));
Note, though, that, because we're dealing with JSON, you will not be able to store methods as part of this object. They will be stripped out by JSON.stringify().
Finally, using attributes means you'll muddy your HTML since they show up in any HTML dumps (unlike properties) but this is purely a cosmetic weakness.
You can use the data attribute in jquery to do this (http://api.jquery.com/data/)
You can use html 5 data attributes:
http://ejohn.org/blog/html-5-data-attributes/
And these can be retrieved using the jquery data api.
These will have to be stringifed however
"As of jQuery 1.4.3 HTML 5 data- attributes will be automatically pulled in to jQuery's data object"

What is the advantage of using 'data-attribute' over $.data in jQuery?

I was wondering what the advantage is of using data assignment to the DOM vs. NOT to the DOM
i.e. Assume we have said HTML
<a id="foo" href="#">foo!</a>
//Set attr data-me so it's <a id="foo" data-me="yay" href="#">foo!</a>
$('#foo').attr('data-me', 'yay');
//Retrieve the data 'yay'
$('#foo').data('data-me');
Over and above direct assignment:
var myInput = $('#foo');
//Add some data
$.data(myInput, 'data-me', 'yay');
//Retrieve the data 'yay'
$.data(myInput, 'data-me');
My understanding is that the later is MUCH faster and therefore I can't see the sense in adding data-someAttr all over the DOM when it's not required ?
They serve different purposes. Yes, it seems like they can be used to achieve the same thing, but under the hood there are differences.
While you can save simple values in attributes, you can not save DOM nodes, because you will create memory leaks. Neither can you save objects, arrays, functions, etc...
$.attr() might be faster than $('selector').data(), but it is not faster than the low-level method jQuery.data(). The first data method has much more overhead than the second, however the second one does not carry all the functionality of the first one. For example, it does not get the data out of the data- attributes.
Thus, I think it's best to use data- attributes at load time, so you can extract the data with $('selector').attr(), and handle the application state with $.data().
I'm not a guru, that' s for sure... but it appears to me that two obvious differences are the time/environment when the 'data' is set, and the structure(type) of data that will be stored/accessed.
Concider this scenario:
<a id="foo" data-me="some string data" href="#">foo!</a
This pice of html was maybe generated on the server side. If so,only the server side needs to know about the 'data-me' value origin. One limitation is that the attribute must be a string , or a string representation of an object (JSON)
var customData =
{
date:new Date(),
otherProp:'some value',
someFunc: function(a,b)
{
// function dody
}
};
$('#foo').attr('data-me', customData)
From the javascript(medium), at a certain moment, triggered by a certain event(time), you can use jQuery to bind a dom element to a given object (not necessarily a string)
So the latter way ($('#foo').attr('data-me', customData)), removes the 'only string' limitation and allows you to bind to a dom element any kind of js object , even functions.

document.getElementById vs jQuery $()

Is this:
var contents = document.getElementById('contents');
The same as this:
var contents = $('#contents');
Given that jQuery is loaded?
Not exactly!!
document.getElementById('contents'); //returns a HTML DOM Object
var contents = $('#contents'); //returns a jQuery Object
In jQuery, to get the same result as document.getElementById, you can access the jQuery Object and get the first element in the object (Remember JavaScript objects act similar to associative arrays).
var contents = $('#contents')[0]; //returns a HTML DOM Object
No.
Calling document.getElementById('id') will return a raw DOM object.
Calling $('#id') will return a jQuery object that wraps the DOM object and provides jQuery methods.
Thus, you can only call jQuery methods like css() or animate() on the $() call.
You can also write $(document.getElementById('id')), which will return a jQuery object and is equivalent to $('#id').
You can get the underlying DOM object from a jQuery object by writing $('#id')[0].
Close, but not the same. They're getting the same element, but the jQuery version is wrapped in a jQuery object.
The equivalent would be this
var contents = $('#contents').get(0);
or this
var contents = $('#contents')[0];
These will pull the element out of the jQuery object.
A note on the difference in speed. Attach the following snipet to an onclick call:
function myfunc()
{
var timer = new Date();
for(var i = 0; i < 10000; i++)
{
//document.getElementById('myID');
$('#myID')[0];
}
console.log('timer: ' + (new Date() - timer));
}
Alternate commenting one out and then comment the other out. In my tests,
document.getElementbyId averaged about 35ms (fluctuating from 25ms up to 52ms on about 15 runs)
On the other hand, the
jQuery averaged about 200ms (ranging from 181ms to 222ms on about 15 runs).
From this simple test you can see that the jQuery took about 6 times as long.
Of course, that is over 10000 iterations so in a simpler situation I would probably use the jQuery for ease of use and all of the other cool things like .animate and .fadeTo. But yes, technically getElementById is quite a bit faster.
No. The first returns a DOM element, or null, whereas the second always returns a jQuery object. The jQuery object will be empty if no element with the id of contents was matched.
The DOM element returned by document.getElementById('contents') allows you to do things such as change the .innerHTML (or .value) etc, however you'll need to use jQuery methods on the jQuery Object.
var contents = $('#contents').get(0);
Is more equivilent, however if no element with the id of contents is matched, document.getElementById('contents') will return null, but $('#contents').get(0) will return undefined.
One benefit on using the jQuery object is that you won't get any errors if no elements were returned, as an object is always returned. However you will get errors if you try to perform operations on the null returned by document.getElementById
No, actually the same result would be:
$('#contents')[0]
jQuery does not know how many results would be returned from the query. What you get back is a special jQuery object which is a collection of all the controls that matched the query.
Part of what makes jQuery so convenient is that MOST methods called on this object that look like they are meant for one control, are actually in a loop called on all the members int he collection
When you use the [0] syntax you take the first element from the inner collection. At this point you get a DOM object
In case someone else hits this... Here's another difference:
If the id contains characters that are not supported by the HTML standard (see SO question here) then jQuery may not find it even if getElementById does.
This happened to me with an id containing "/" characters (ex: id="a/b/c"), using Chrome:
var contents = document.getElementById('a/b/c');
was able to find my element but:
var contents = $('#a/b/c');
did not.
Btw, the simple fix was to move that id to the name field. JQuery had no trouble finding the element using:
var contents = $('.myclass[name='a/b/c']);
var contents = document.getElementById('contents');
var contents = $('#contents');
The code snippets are not the same. first one returns a Element object (source).
The second one, jQuery equivalent will return a jQuery object containing a collection of either zero or one DOM element. (jQuery documentation). Internally jQuery uses document.getElementById() for efficiency.
In both the cases if more than one element found only the first element will be returned.
When checking the github project for jQuery I found following line snippets which seems to be using document.getElementById codes (https://github.com/jquery/jquery/blob/master/src/core/init.js line 68 onwards)
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
Just like most people have said, the main difference is the fact that it is wrapped in a jQuery object with the jQuery call vs the raw DOM object using straight JavaScript. The jQuery object will be able to do other jQuery functions with it of course but, if you just need to do simple DOM manipulation like basic styling or basic event handling, the straight JavaScript method is always a tad bit faster than jQuery since you don't have to load in an external library of code built on JavaScript. It saves an extra step.
One other difference: getElementById returns the first match, while $('#...') returns a collection of matches - yes, the same ID can be repeated in an HTML doc.
Further, getElementId is called from the document, while $('#...') can be called from a selector. So, in the code below, document.getElementById('content') will return the entire body but $('form #content')[0] will return inside of the form.
<body id="content">
<h1>Header!</h1>
<form>
<div id="content"> My Form </div>
</form>
</body>
It might seem odd to use duplicate IDs, but if you are using something like Wordpress, a template or plugin might use the same id as you use in the content. The selectivity of jQuery could help you out there.
All the answers are old today as of 2019 you can directly access id keyed filds in javascript simply try it
<p id="mytext"></p>
<script>mytext.innerText = 'Yes that works!'</script>
Online Demo!
- https://codepen.io/frank-dspeed/pen/mdywbre
jQuery is built over JavaScript. This means that it's just javascript anyway.
document.getElementById()
The document.getElementById() method returns the element that has the ID attribute with the specified value and Returns null if no elements with the specified ID exists.An ID should be unique within a page.
Jquery $()
Calling jQuery() or $() with an id selector as its argument will return a jQuery object containing a collection of either zero or one DOM element.Each id value must be used only once within a document. If more than one element has been assigned the same ID, queries that use that ID will only select the first matched element in the DOM.
All the answers above are correct. In case you want to see it in action, don't forget you have Console in a browser where you can see the actual result crystal clear :
I have an HTML :
<div id="contents"></div>
Go to console (cntrl+shift+c) and use these commands to see your result clearly
document.getElementById('contents')
>>> div#contents
$('#contents')
>>> [div#contents,
context: document,
selector: "#contents",
jquery: "1.10.1",
constructor: function,
init: function …]
As we can see, in the first case we got the tag itself (that is, strictly speaking, an HTMLDivElement object). In the latter we actually don’t have a plain object, but an array of objects. And as mentioned by other answers above, you can use the following command:
$('#contents')[0]
>>> div#contents

Categories