I am a little confused how jQuery stores data with .data() functions.
Is this something called expando?
Or is this using HTML5 Web Storage although I think this is very unlikely?
The documentation says:
The .data() method allows us to attach data of any type to DOM elements in a way that is safe from circular references and therefore from memory leaks.
As I read about expando, it seems to have a risk of memory leak. Unfortunately my skills are not enough to read and understand jQuery code itself, but I want to know how jQuery stores such data by using data().
http://api.jquery.com/data/
Basically jQuery holds the information you store/retrieve with data(name, value)/data(name) and remove with removeData(name) in an internal javascript object named cache. The rest is just a bit of javascript magic to make it work and keep all the associations right.
Oh and to answer the first part of your question. Neither is it expando nor HTML5 WebStorage.
To correct myself I think jQuery does use expando in one occasion. It sets one attribute on those elements you used data() on to store information to them. The attribute name looks like this
"jQuery" + now() //e.g. jQuery1268647073375
btw. now() is an internal function which returns (new Date).getTime()
and the value is an UUID generated by jQuery.
This way jQuery later on can retrieve the correct associated data from it's internal cache.
So if you are concerned about expando in IE, where I recall you can't delete them, then the leak should be minimal as jQuery only uses 1 expando per element you store data on. Unless you call data() on literally 1000s of elements I see no memory problems
Function data in jQuery.fn.extend is using this statement to save provided variable:
jQuery.cache[ id ][ name ] = data;
jQuery.cache is just a standard object, defined as cache: {}, inside jQuery namespace.
So answering your question - I believe jQuery stores data in standard, internal JavaScript object called cache.
Oh, and regarding your memory leak question - I really don't know. If JavaScript has some troubles storing references to DOM elements in standard JS object this might be an issue.
Also check out the metadata plugin - it extracts metadata from a DOM Element and returns it as an object (discussed in comments here).
Its put into cache by the browser locally much like a cookie
from jquery uncompressed:
if ( data !== undefined ) {
thisCache[ name ] = data;
}
Related
Is it possible to add a JavaScript object which is found while debugging, to the DOM for future reference? (It is okay to keep it in any place where retrievable. DOM is just a suggestion. Storing and retrieving needs to be done using a debugging tool.)
(Sample usage:pass as a argument by retrieving it from DOM while calling to a function using console.)
Note : This is not about how to add a JavaScript Object to the DOM within JS code. I'm aware that it can be done using methods mentioned here.
I'm using JSON.stringify and JSON.parse to store and retrieve objects from localStorage. However, it appears that JSON.stringify strips out the instance functions from the object. Thus, after JSON.parse, I can no longer call myObject.doSomething(). I know that I can attach this function manually: myObject.doSomething = MyClass.prototype.myFunction, but that'll be troublesome if this action is repeated many times in the web app. How do people normally do this in JavaScript?
JSON obviously does not hold onto the functions themselves is only stores simple typed variables. The way I have addressed this in the pass is to be a restore method in my class and simply call that method with the data from JSON so as to re-populate the class with the data that belongs in it.
I have done this extensively with the Value Object ( VO ) design pattern in my code base and it has worked quite well for me. Just a word of a caution though, Ie7/Ie8 are not terribly friendly with this approach if you try to communicate across windows. As I recall I think it is IE7 that does not return the right "typeof" for some properties so I ran into a whole bunch of challenges in my restore when cross-window communication was involved.
I have a particular scenario where I need a file name, not once but twice, because I pass the variable to an ASP.NET MVC controller. Is it a good idea to either store the file name in a DOM element like a span or div, and getting the value by using jQuery's .text() function? Or would a better approach be to work with JSON-like objects that are initialized and then continuously manipulated?
The question however remains. Is it a good or bad idea to store variables in HTML DOM elements?
As #Atticus said, it's fine to do it either way, and I'll do both depending on what I need the data for: If it's specifically tied to the element, I'll store it on the element; if it's more general to the page, I'll pass back an object using JSON notation.
When storing data on DOM elements, you don't need to store them as text within the element. You can use data-* attributes instead. These are valid as of HTML5 and work in all browsers right now. The only downside is that if you're using validation as part of your workflow, and you're not yet using HTML5 to validate (and that wouldn't be surprising, the validator isn't quite ready, what with the spec still being rather in flux!), they don't validate in HTML 4.01 or below. But browsers are fine with them, this is one of the areas where HTML5 is codifying (and reigning in) current practice, rather than innovating.
Either one works, and it's fine to store data in a DOM. It more so depends on the complexity of the operation you are trying complete, which sounds simple -- storing file names. I think you should be fine doing it this way. Storing in JSON object works too, I would go with whatever fits your structure best and which ever works easier with your client/server handshake.
Is there any benefit in using the data method when I can just store variables in plain javascript objects? It seems to me that using the jQuery data method is performance downgrade if it means re-looking up the DOM element to retrieve the value of the key, if the DOM element has not been referenced prior?
Sorry if this is painfully obvious to javascript devs, but I hope to understand this fully.
It's simply a case of convenience and it depends on your implementation as to whether you would use the data functionality. You would normally use .data() when you wanted to store related values for a specific DOM element or, more likely, a "collection" of DOM elements. You would normally then retrieve those values as you iterate through the elements and make a decision or perform an action based on the value. Even if you are going to retrieve one value - if you cached the jQuery DOM element(s) it would be an inexpensive call to retrieve the attached value.
Because you cannot easily store data related to a certain DOM element using "plain javascript objects". If your data is not related to a certain DOM object you shouldn't use $.data of course!
I figured.
The DOM element can and should be referenced prior when using data, and that wouldn't result in a performance hit thus (and we still maintain the DOM_Element-key-value relationship).
So the initial look up for 'body' when storing the key-value is the only performance cost, and subsequent data retrievals don't have to find 'body' again:
b = $('body');
b.data('key', 'value');
alert(b.data('key'));
I recently upgraded our project's jQuery file from 1.4.2 to 1.4.4 and it appears that as of 1.4.3 the way we have been using jQuery.data has stopped working.
We have this code:
var events = $(window).data('events');
if (events.scroll)
if (!events.scroll.include(handler))
$(window).scroll(handler);
the purpose is to prevent this particular handler from being bound multiple times.
In 1.4.2, this works fine. In 1.4.4, events is undefined.
function handler() {
//do something
}
$(document).ready(function(){
$(window).scroll(handler);
$('div#test').scroll(handler);
$(window).data('events') -> undefined
$('div#test').data('events') -> Object
});
What changed with this API? How should I list events for window?
I have changed the first line to this:
var events = $(window).data('__events__').events;
a bit messy-looking, but the ability to wire events to plain objects is compelling.
There was a change in jQuery 1.4.3+ for event types, to avoid object name collisions, for window (or any other plain object) use the key "__events__" instead, like this:
var events = $(window).data('__events__');
The same __events__ key is used for any objects that don't have a .nodeType property (which window doesn't, so it's treated like a plain object here).
To be clear that this was a conscious, intentional change, it's included in the jQuery 1.4.3 release notes:
JavaScript Objects
A number of changes were made to when .data() is used on JavaScript objects (or, more accurately, anything that isn’t a DOM node). To start whenever you set data on a JavaScript object the data is set directly on the object – instead of going into the internal data object store. Additionally events that are attached to objects are put in a new __events__ property that is actually a function. This was done to allow events to be attached directly to an object, be garbage collected when the object is collected, and not be serialized by a JSON serializer. These changes should make jQuery’s data and event systems much more useful on JavaScript objects.
The basic API still seems to work.
However, it doesn't seem to work on the window.
So, the API for accessing jQuery-assigned events hasn't really changed; it just no longer applies to the window. That doesn't exactly sound like an intentional decision, and the 1.4.3 -> 1.4.4 changelog makes no mention of it.
Sounds like a bug, and it might have to do with the recent changes to data now being able to access HTML5 data- attributes. Consider filing a ticket for it :/