最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin2浏览0评论

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.

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.

Share Improve this question edited Mar 21, 2017 at 2:01 Makyen 33.3k12 gold badges92 silver badges125 bronze badges asked Aug 13, 2014 at 12:42 BraiamBraiam 4,49611 gold badges49 silver badges83 bronze badges 12
  • 6 That's actually an implementation detail. Data stored through jQuery's data() setter is not stored in the DOM, but in an internal cache associated with the element. You would have to duplicate quite a lot of jQuery's implementation to retrieve that data in plain JS, and that implementation can change in the future, so I don't think this would be a good idea. – Frédéric Hamidi Commented Aug 13, 2014 at 12:45
  • You can't get at those properties without the jQuery object as that's where they're stored. This answer shows how to get it out of the internal representation, in case you're interested – blgt Commented Aug 13, 2014 at 12:51
  • 3 @Braiam What's the point of trying to access something in another way than the prescribed $(...).data way? It's like saying, you would like to use some API's features, but without going through that API. Pointless? – plalx Commented Aug 13, 2014 at 12:53
  • @FrédéricHamidi You are right, that it's stored internally, however it's quite easy to access. – dfsq Commented Aug 13, 2014 at 13:00
  • 1 I 100% agree that one should not access data other then via $.data API. But if OP wants to know where jQuery stores those values, the answer is simple: event handlers and data are stored in $.cache. – dfsq Commented Aug 13, 2014 at 13:07
 |  Show 7 more comments

2 Answers 2

Reset to default 11

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.

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.

  2. try to leak with data

发布评论

评论列表(0)

  1. 暂无评论