Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With vanilla JavaScript, how can I access data stored by jQuery's .data() method?

And before someone says:

document.querySelector('.myElem').getAttribute('data-*')

No, is not what I'm looking for. jQuery's data() has dual functions. 1) It queries an HTML5 data attribute which can be seen in the HTML code like this:

<div data-role="page" data-last-value="43" data-hidden="true" data-options='{"name":"John"}'></div>
$( "div" ).data( "role" ) === "page";
$( "div" ).data( "lastValue" ) === 43;
$( "div" ).data( "hidden" ) === true;
$( "div" ).data( "options" ).name === "John";

and 2) the ones that are set by .data(name, value) which are hidden in a jQuery internal Object, to which the documentation only says "to save information under the names 'events' and 'handle'", yet I haven't figured out a way to access them or what actually creates them.

So, my question stands, how can I access the jQuery data values from plain JavaScript?

Just so it's absolutely clear... let me put more emphasis: I don't want the data- HTML attributes, but the jQuery object data.

like image 395
Braiam Avatar asked Aug 13 '14 12:08

Braiam


People also ask

What can vanilla JavaScript do?

"VanillaJS is a name to refer to using plain JavaScript without any additional libraries like jQuery back in the days. People use it as a joke to remind other developers that many things can be done nowadays without the need for additional JavaScript libraries." Or, in our case, without new, fancy frameworks.

How have you stored and referenced data in your jQuery code?

The data() is an inbuilt method that store the arbitrary data related to the matching elements or the value for the named data will be returned, which contains the first element in the set of matching elements. Syntax: $(selector). data(element);

Should you use vanilla JavaScript?

Conclusion. Whether you should use Vanilla JS or React depends very much on your use case. Vanilla JS is awesome but it's not a great alternative when it comes to building huge applications with complex dynamic functionalities. Besides, it cannot create complex and efficient UIs.


2 Answers

jQuery uses internal object called $.cache to store event handlers and data values. $.cache is a simple dictionary which can look something like this:

{ 1: { data: { role: "page"}, events: { click: ...} }, 2: { ... }}

The keys are all unique indexes corresponding to some DOM nodes. If your DOM element was previously touched by jQuery (attached events or to some data), you will see that it has a weird property similar to the one below:

jQuery17108624803440179676: 2

Here jQuery17108624803440179676 is a unique string generated by jQuery when it was loaded on the page. This unique identifier is called "expando" and is stored as

$.expando

Now that you know how to access an individual element's internal data by bypassing jQuery's data API...

$.cache[element[$.expando]]

...you may ask why jQuery uses an intermediate object to store all DOM data and events. The reason is that it is a way to avoid memory leaks, which would be the case if data and events handlers were stored directly in DOM elements properties (due to circular references).

Having said all that, I want to emphasize that you should not work with node data objects other than that via the jQuery Data API for the simple reason that it handles all the complex stuff behind the scenes.

like image 118
dfsq Avatar answered Oct 10 '22 19:10

dfsq


From the comments, your motivation to bypass $(...).data seems to be based on the fact that it's causing performance issues.

I totally agree with @meagar on that point, $(...).data shouldn't be expensive enough to cause bottlenecks. However, if you continously re-query and re-wrap DOM elements as jQuery elements (e.g. $('#someEl').data(...) multiple times rather than caching $('#someEl') and do $someEl.data(...), then it might be an issue.

Please also note that if you find yourself attaching a lot of data to DOM elements, you probably got your design wrong. Your data shouldn't live in the presentation layer and you shouldn't have to query the DOM or get a reference to a DOM element to access your data. Obviously, you might in some situations, but these shouldn't be the norm.

If you still want to build your own data feature, then here's an example. It's not optimized, but you should get the idea:

Note that WeakMap is only available in modern browsers, but without it we would be leaking memory, unless you provide a mechanism to destroy the cached attributes expendo object when the associated DOM element gets destroyed.

JSFIDDLE

JSPERF (the test might not be fair, since I'm not sure my implementation does everyting $(...).data does)

var data = (function () {
    var attributes = new WeakMap();

    return function (el, attr, val) {
        var elAttrs = attributes.get(el),
            isSetOperation = arguments.length > 2;

        if (isSetOperation) {
            if (!elAttrs) attributes.set(el, elAttrs = {});
            elAttrs[attr] = val;
        } else {
            return datasetOrCachedAttrsValue();
        }

        function datasetOrCachedAttrsValue() {
            var attrVal = el.dataset[attr];

            return typeof attrVal !== 'undefined'?
                attrVal: 
                elAttrs && elAttrs[attr];
        }
    };
})();

var div = document.querySelector('div');

console.log(data(div, 'test')); //test
console.log(data(div, 'notexisting')); //undefined
data(div, 'exists', true);
console.log(data(div, 'exists')); //true

"would be leaking memory, unless you provide a mechanism to destroy the cached" - plalx. Well even with WeakMap it still leaks for the same reason why jQuery can cause leaks. See this demo. – dfsq

That was a good concern from @dfsq, but here's a demo that actually shows how WeakMap allows garbage collection once they key is unreachable, as opposed to jQuery's implementation that will hold on data (unless $().remove is used I believe).

Use the profiler and record heap allocations over time and compare the results. Here's what I got from 6 clicks on each button (one type of button per snapshot). We can clearly see that $().data is leaking while the custom data implementation using a WeakMap is not.

  1. try to leak with $().data.

    enter image description here

  2. try to leak with data

    enter image description here

like image 44
plalx Avatar answered Oct 10 '22 17:10

plalx