Stoyan (@stoyanstefanov) is a Facebook engineer, former Yahoo!, writer ("JavaScript Patterns", "Object-Oriented JavaScript"), speaker (JSConf, Velocity, Fronteers), toolmaker (Smush.it, YSlow 2.0) and a guitar hero wannabe.
HTML5 introduces custom data
attributes: non-visible pieces of information associated with a DOM node. This is an extremely convenient feature, especially since these attributes are accessible by both JavaScript and CSS. But what about performance? Is it possible that the use of data
attributes will make your application run slower?
Accessing data
Let’s say you have an element that looks like:
<divid="mydiv"data-purpose="greeting">Hello</div>
With HTML5 you can access the custom attribute with
mydiv.dataset.purpose; // "greeting"
For older browsers you need to fall back to using getAttribute()
:
mydiv.getAttribute('data-purpose'); // "greeting"
DOM is slow
You know that DOM operations are a common bottleneck in dynamic web application, more so than anything you do in ECMAScript land (functions, objects, loops). And these data
attributes hang on DOM nodes. Does that mean that reading and writing them in JavaScript can be slow?
Let’s write a JSPerf.com test to find out.
DIY Data utility
In the test we compare the performance of:
- using HTML5
dataset
- using the fallback old school
getAttribute()
andsetAttribute()
- using a do-it-yourself data storage using a small
Data
object
The Data
utility creates one custom attribute __data
for each DOM element we’re interested in and uses it as an auto-incremented identifier. The actual data will be stored privately in the Data
object’s closure in a warehouse
variable:
varData = function(){varwarehouse = {}; varcount = 1; return{reset: function(){count = 1; warehouse = {}; }, set: function(dom, data){if(!dom.__data){dom.__data = "hello" + count++; }warehouse[dom.__data] = data; }, get: function(dom){returnwarehouse[dom.__data]; }}; }();
(Side note: creating custom (expando) attributes is associated with memory leaks in IE, so it’s a good idea to cleanup all these expandos before you remove the DOM node or unload the page.)
Using this utility looks like:
Data.set(mydiv, {purpose: "greeting"}); Data.get(mydiv).purpose; // "greeting"
The test
You can run the test at jsperf.com/data-attributes.
It uses one DOM node, and writes and reads three data properties using each of the three methods: old school, dataset
and DIY.
// old school attributesdiv.setAttribute('data-po', 'to'); div.setAttribute('data-ta', 'ma'); div.setAttribute('data-to', 'to'); vara = div.getAttribute('data-po'); varb = div.getAttribute('data-ta'); varc = div.getAttribute('data-to'); // HTML5 datasetdiv.dataset.po = 'to'; div.dataset.ta = 'ma'; div.dataset.to = 'to'; vardata = div.dataset; vara = data.po; varb = data.ta; varc = data.to; // DIY Data utilityData.set(div, {po: 'to', ta: 'ma', to: 'to'}); vardata = Data.get(div); vara = data.po; varb = data.ta; varc = data.to;
Results
The results show that using a simple Data
utility to store data associated with a DOM node is significantly faster than storing the data in the DOM node. And the results are consistent across browsers.
Using data
attributes can be anywhere between 5 to 30 times slower than using a Data
object.
Interestingly, HTML5 dataset
is the slowest, even slower than using getAttribute()
, although the big difference really is between a data
attribute and a Data
object in ECMAScript land.
Takeaways
Using data attributes is convenient, but if you’re doing a lot of reading and writing, your application’s performance will suffer. In such cases it’s better to create a small utility in JavaScript to store and associate data about particular DOM nodes.
In this article, you saw a very simple Data
utility. You can always make it better. For example, for convenience you can still produce markup with data
attributes and the first time you use Data.get()
or Data.set()
, you can carry over the values from the DOM to the Data
object and then use the Data
object from then on.