Save object states in .data or attr - Performance vs CSS? - javascript

In response to my answer yesterday about rotating an Image, Jamund told me to use .data() instead of .attr()
First I thought that he is right, but then I thought about a bigger context... Is it always better to use .data() instead of .attr()? I looked in some other posts like what-is-better-data-or-attr or jquery-data-vs-attrdata
The answers were not satisfactory for me...
So I moved on and edited the example by adding CSS. I thought it might be useful to make a different Style on each image if it rotates. My style was the following:
.rp[data-rotate="0"] {
border:10px solid #FF0000;
}
.rp[data-rotate="90"] {
border:10px solid #00FF00;
}
.rp[data-rotate="180"] {
border:10px solid #0000FF;
}
.rp[data-rotate="270"] {
border:10px solid #00FF00;
}
Because design and coding are often separated, it could be a nice feature to handle this in CSS instead of adding this functionality into JavaScript. Also in my case the data-rotate is like a special state which the image currently has. So in my opinion it make sense to represent it within the DOM.
I also thought this could be a case where it is much better to save with .attr() then with .data(). Never mentioned before in one of the posts I read.
But then i thought about performance. Which function is faster? I built my own test following:
<!DOCTYPE HTML>
<html>
<head>
<title>test</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
function runfirst(dobj,dname){
console.log("runfirst "+dname);
console.time(dname+"-attr");
for(i=0;i<10000;i++){
dobj.attr("data-test","a"+i);
}
console.timeEnd(dname+"-attr");
console.time(dname+"-data");
for(i=0;i<10000;i++){
dobj.data("data-test","a"+i);
}
console.timeEnd(dname+"-data");
}
function runlast(dobj,dname){
console.log("runlast "+dname);
console.time(dname+"-data");
for(i=0;i<10000;i++){
dobj.data("data-test","a"+i);
}
console.timeEnd(dname+"-data");
console.time(dname+"-attr");
for(i=0;i<10000;i++){
dobj.attr("data-test","a"+i);
}
console.timeEnd(dname+"-attr");
}
$().ready(function() {
runfirst($("#rp4"),"#rp4");
runfirst($("#rp3"),"#rp3");
runlast($("#rp2"),"#rp2");
runlast($("#rp1"),"#rp1");
});
</script>
</head>
<body>
<div id="rp1">Testdiv 1</div>
<div id="rp2" data-test="1">Testdiv 2</div>
<div id="rp3">Testdiv 3</div>
<div id="rp4" data-test="1">Testdiv 4</div>
</body>
</html>
It should also show if there is a difference with a predefined data-test or not.
One result was this:
runfirst #rp4
#rp4-attr: 515ms
#rp4-data: 268ms
runfirst #rp3
#rp3-attr: 505ms
#rp3-data: 264ms
runlast #rp2
#rp2-data: 260ms
#rp2-attr: 521ms
runlast #rp1
#rp1-data: 284ms
#rp1-attr: 525ms
So the .attr() function did always need more time than the .data() function. This is an argument for .data() I thought. Because performance is always an argument!
Then I wanted to post my results here with some questions, and in the act of writing I compared with the questions Stack Overflow showed me (similar titles)
And true enough, there was one interesting post about performance
I read it and run their example. And now I am confused! This test showed that .data() is slower then .attr() !?!! Why is that so?
First I thought it is because of a different jQuery library so I edited it and saved the new one. But the result wasn't changing...
So now my questions to you:
Why are there some differences in the performance in these two examples?
Would you prefer to use data- HTML5 attributes instead of data, if it represents a state? Although it wouldn't be needed at the time of coding? Why - Why not?
Now depending on the performance:
Would performance be an argument for you using .attr() instead of data, if it shows that .attr() is better? Although data is meant to be used for .data()?
UPDATE 1:
I did see that without overhead .data() is much faster. Misinterpreted the data :) But I'm more interested in my second question. :)
Would you prefer to use data- HTML5 attributes instead of data, if it
represents a state? Although it wouldn't be needed at the time of
coding? Why - Why not?
Are there some other reasons you can think of, to use .attr() and not .data()? e.g. interoperability? because .data() is jquery style and HTML Attributes can be read by all...
UPDATE 2:
As we see from T.J Crowder's speed test in his answer attr is much faster then data! which is again confusing me :) But please! Performance is an argument, but not the highest! So give answers to my other questions please too!
UPDATE 3:
My test seems to be false because of the fire-bug I used while testing! The same file in chrome listed attr faster and a second test on jsperf also says attr is faster

This performance part of the question screams of premature optimization; see below. (Lest you get the wrong idea: I too am frequently guilty of wondering about the same sort of premature optimization question.)
But getting performance out of the way (other points addressed below the graph): As far as I can see, attr is faster than data in jQuery 1.7.1: http://jsperf.com/jquery-setting-attr-vs-data This surprises me. Not that it's remotely likely to matter.
Gratuitous bar graph (longer lines = faster performance):
Are there some other reasons you can think of, to use .attr() and not .data()?
At least a couple come to mind:
The advantage of data is that it doesn't have to write to the element every time; you only write to the actual element the first time, and from then on jQuery is just updating a value in a JavaScript object it maintains in a separate object cache (connected to the element via a key). (I'm not sure why it's slower than attr; perhaps because of the indirection.)
One thing I dislike about data is that it's not symmetrical: The first time you access data on an element, the data object is seeded with data-* attributes from the element; but from there on out, there is no connection between the two.
Example (live copy | live source):
var target = $("#target");
display("data('foo'): " + target.data("foo"));
display("data-foo: " + target.attr("data-foo"));
display("Setting data('foo')");
target.data("foo", "updated data('foo')");
display("data('foo'): " + target.data("foo"));
display("data-foo: " + target.attr("data-foo"));
display("Setting data-foo");
target.attr("data-foo", "updated data-foo");
display("data('foo'): " + target.data("foo"));
display("data-foo: " + target.attr("data-foo"));
Assuming the #target element starts out with data-foo="bar", the output is:
data('foo'): bar
data-foo: bar
Setting data('foo')
data('foo'): updated data('foo')
data-foo: bar
Setting data-foo
data('foo'): updated data('foo')
data-foo: updated data-foo
That can be confusing and surprising. The way you have to think about it is that the data-* attributes are default values only. I just don't like how they're so dependent on whether you've called data before or not; unless you never write to the data-* attribute directly, you can't be sure what value data will get (the original from the markup, or a value you updated later before you called data). It seems a bit chaotic to me, but if you set yourself rules (never write to data-* attributes directly and only ever use data, for instance), you can avoid the chaos.
When you use attr, you can only store strings. When you use data, you can store any JavaScript value or object reference.
Because performance is always an argument!
Not in 2012. :-) Or at least, it's a lot lower down the list relative to other arguments than it used to be absent a specific, demonstrable performance problem.
Let's look at your runfirst #rp4 results: 10k iterations of attr took 515ms; 10k iterations of data took 268ms. That's 51.5 usec (microseconds, millionths of a second) each vs. 26.8 usec each. So you're wondering whether to use data if it saves you 24.7 usec per operation. Humans perceive things on the order of tenths of seconds. So for it to matter, you have to do this op roughly 4,000 times in a tight loop for a human to notice the difference. That's just not even close to worth worrying about, even in a mousemove handler.
If you're into that kind of territory (4,000/second in a tight loop), you'll probably want to avoid storing the information on the element at all.

Given that .data() is indeed slower than .attr() on most browsers, but the speed difference is not important in this question, one advantage of data() over attr() is that data will automatically coerce data- attributes to numbers or to boolean values if they match.
That means that
<div id="boolean" data-t="true" data-f="false">
will result in boolean runtime value of true & false when you run this:
console.log($('#boolean').data('t')); // reports true (not a string)
console.log($('#boolean').data('f')); // reports false (not a string)
and
<div id="number" data-n="123.456">
will result in a number runtime value of 123.456 when you run this:
console.log($('#number').data('n')); // Reports 123.456 (not a string)
attr on the other hand only works with strings, but will convert value to strings for saving. It will not coerce values when you fetch them.
The choice between attr and data depends on the feature you need for a specific example:
Where I inject data- settings from the server into pages, I tend to use data() to access them, if only because it is shorter code.
If I need the data to be visible in the DOM, I will use attr() to save the values`.

You may use jQuery.data. It's almost always the fastest. jQuery tries to optimize its function on every browsers, and maximize compability. So with new versions of jQuery, you may gain performance for this function.
This 2nd test gave me jQuery.data as the winner.

Related

How should I use Variables and jQuery Dom navigation?

I was just wondering which is the correct or most efficient way of navigating through the Dom using variables.
For example, can I concatenate selectors
var $container = '.my-container';
$($container).addClass('hidden');
$($container + ' .button').on('click', function(){
//something here
});
or should I use the jQuery traversal functions
var $container = $('.my-container');
$container.addClass('hidden');
$container.children('.button').on('click', function(){
//something here
});
Is there a different approach, is one best, or can you use them at different times?
The $ is usually used only when working with an actual jquery object. You generally shouldn't prefix anything with that unless it's really something from jquery.
Beyond that little bit though, performance-wise, your second bit of code is going to be faster. I made an example jsperf here: http://jsperf.com/test-jquery-select
The reason the second bit of code is faster is because (if I remember correctly) jquery caches the selection, and then any actions performed on that selection are scoped. When you use .find (which is really what you meant in your code, not .children), instead of trying to find elements through the entire document, it only tries to find them within the scope of whatever my-container is.
The time when you wouldn't want to use the second pattern is when you expect the dom to change frequently. Using a previous selection of items, while efficient, is potentially a problem if more buttons are added or removed. Granted, this isn't a problem if you're simply chaining up a few actions on an item, then discarding the selection anyway.
Besides all of that, who really wants to continuously type $(...). It's awkward.

Another puzzling behavior of getElementById()

Try this script: http://jsfiddle.net/8da54/1/. Why is the output different in Firefox and IE?
HTML:
<div id="div1">
<div id="div2"><div id="data">data</div></div>
<div id="div3">a-</div>
</div>
Javascript:
var data = document.getElementById("data");
document.getElementById("div2").innerHTML="";
document.getElementById("div3").appendChild(data);
Output:
FF: a-data
IE: a-
Appears to be one of the many IE .innerHTML bugs. In the meantime, you could remove elements the proper way:
var elem = document.getElementById("div2");
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
http://jsfiddle.net/8da54/2/
Looking in to the question you're asking.
Why is the two browsers FF and IE, behaving differently regarding the three lines of JavaScript.
JavaScript usually makes copies of value types and references to objects.
In this particular case, depending on browser, the engines disagree on the implementation.
So when in doubt whether you're copying or referencing. Clone the object if possible to avoid conflicts.
var data = document.getElementById("data").cloneNode(true);
//lot's of intensive work with the cloned 'data'-object, function calls etc.
document.getElementById("div2").innerHTML="";
document.getElementById("div3").appendChild(data);
This way you are also certain that you have pulled the 'data'-object out of the render-tree and can safely manipulate it in any way, before you insert the result, without to much worry about performance.
My experience is, that it is always a good idea to avoid working directly in the browser-DOM ( render tree) if in any way possible.
Personally I would keep the the two following lines for clarity, since it is quite obvious what their intend is, and readability always enhance maintainability.
Especially when the performance is the same, if not better(IE).

Why shouldn't I access elements more "directly" (elemId.innerHTML)

I've seen some JavaScript code to access HTML elements like this: elementID.innerHTML, and it works, though practically every tutorial I searched for uses document.getElementById(). I don't even know if there's a term for the short addressing.
At first I thought simplistically that each id'ed HTML element was directly under window but using getParent() shows the tree structure is there, so it didn't matter that elements I wanted were nested. I wrote a short test case:
http://jsfiddle.net/hYzLu/
<div id="fruit">Mango<div id="color">red</div></div>
<div id="car">Chevy</div>
<div id="result" style="color: #A33"></div>
result.innerHTML = "I like my " + color.innerHTML + " " + car.innerHTML;
The "short" method looks like a nice shortcut, but I feel there is something wrong with it for it practically not appearing in tutorials.
Why is document.getElementById() preferred, or may be even required in some cases?
Why shouldn't I access elements more “directly” (elemId.innerHTML)
Because, according to the others in this thread, referencing arbitrarily by id name is not fully supported.
So, what I think you should be doing instead is store their selections into a var, and then reference the var.
Try instead
var color = document.getElementById('color');
color.innerHTML = 'something';
The reason why this would be a good thing to do is that performing a lookup in the DOM is an expensive process, memory wise. And so if you store the element's reference into a variable, it becomes static. Thus you're not performing a lookup each time you want to .doSomething() to it.
Please note that javascript libraries tend to add shim functions to increase general function support across browsers. which would be a benefit to using, for example, jquery's selectors over pure javascript. Though, if you are in fact worried about memory / performance, native JS usually wins speed tests. (jsperf.com is a good tool for measuring speed and doing comparisons.)
It's safer I guess. If you had a variable named result in the same context that you are doing result.HTML I'm pretty sure the browser will throw a wobbler. Doing it in the way of document.getElementById() in this instance would obviously provide you with the associated DOM element.
Also, if you are dynamically adding HTML to the page I may be wrong, but you could also encounter unexpected behaviour in terms of what result is :)
Also I will add that not all ID's can have values that will not work as variable names. For instance if your ID is "nav-menu".
Although I suppose you could write window["nav-menu"].innerHTML
Which makes me think, what happens if you create a window level variable with the same name as an ID?
Checkout this jsfiddle (tested in chrome): http://jsfiddle.net/8yH5y/
This really seems like a bad idea altogether. Just use document.getElementById("id") and store the result to a variable if you will be using the reference more than once.

jQuery(#id).val() vs. getElementById(#id).value

I been searching but I can only find articles talking about one or the other. Which one is better?
I'm making a small web app where performance is not a big concern since there's nothing complex going on.
I considered using jQuery's val() function since maybe it solves some inconsistency I'm not aware of, but getElementById.value IS faster (although the end user won't notice.)
So which one should I use? Is jQuery's non-native method worth the lower performance to gain more compatibility?
The biggest advantage of using jQuery().val() over document.getElementById().value is that the former will not throw an error if no elements are matched, where-as the latter will. document.getElementById() returns null if no elements are matched, where-as jQuery() returns an empty jQuery object, which still supports all methods (but val() will return undefined).
There is no inconsistency when using .value for form elements. However, jQuery.val() standardises the interface for collecting the selected value in select boxes; where as in standard HTML you have to resort to using .options[this.selectedIndex].value.
If you're using <select> elements as well, .value won't work whereas .val() will.
I would not mind about performance of just getting a value. If you want the best performance, perhaps you shouldn't use a library at all.
jQuery does so many nice little error handling things (look below) that I would never write a line of javascript without jquery in a browser again.
First, val works on checkbox groups, selects, gets html, and the
like.
Second, $ lets you use sizzle selectors, so in the future, you can
easily switch between an ID and a CSS path.
Third, your code will be so much easier to read and maintain if you
just use jQuery, that the time you save maintaining your code
outweighs any speedup that you admit your users won't see. Finally,
jQuery is a very popular, very widely used library. They will make
$ and val as fast as they can.
I think using pure Javascript is quicker for the following reasons:
You won't have to learn more than pure js
If you don't want errors, use catch(exeption) (I think...)
You don't have to put in that little extra time to type in the code to initiate jquery
The browser responds quicker if you don't use jquery
Normal js works (in a better way) on checkboxes #johndodo
Thank you for listening to my answer.
I've been looking into the performance differences with this recently and, slightly unsurprisingly, using vanilla JS to grab a value is faster than using jQuery. However, the fallbacks that jQuery provides to prevent errors, like what #Matt mentioned, is very useful. Therefore, I tend to opt for the best of both worlds.
var $this = $(this),
$val = this.value || $this.val();
With that conditional statement, if this.value tries to throw an error, the code falls back to the jQuery .val() method.
Here https://www.dyn-web.com/tutorials/forms/checkbox/same-name-group.php is an implementation for checkboxes, apparently options just need to be named the same with the array brackets notation in the name i.e.: name="sport[]" then yu get the array inJavascript via: var sports = document.forms['demoForm'].elements['sport[]']
I was looking for a selection type field solution without using jQuery and I came across this solution:
The Selection group is an object: HTMLCollection, and it has a lenght method and a selectedOptions property, which allows you to iterate through its label properties to populate an Array with the selected options, which then you can use:
...
vehicleCol = document.getElementById('vehiculo').selectedOptions;
vehiculos = [];
if (vehicleCol !== undefined) {
for (let i = 0; i < vehicleCol.length; i++) {
vehiculos.push(vehicleCol[i].label.toLowerCase())
}
}
...
I'd use jQuery's val(). Shorter code means faster download time (in my opinion).

What jQuery annoyances should I be aware of as a Prototype user?

We're considering switching our site from Prototype to jQuery. Being all-too-familiar with Prototype, I'm well aware of the things about Prototype that I find limiting or annoying.
My question for jQuery users is: After working with jQuery for a while, what do you find frustrating? Are there things about jQuery that make you think about switching (back) to Prototype?
I think the only that gets me is that when I do a selection query for a single element I have to remember that it returns an array of elements even though I know there is only one. Normally, this doesn't make any difference unless you want to interact with the element directly instead of through jQuery methods.
Probably the only real issue I've ever ran into is $(this) scope problems. For example, if you're doing a nested for loop over elements and sub elements using the built in JQuery .each() function, what does $(this) refer to? In that case it refers to the inner-most scope, as it should be, but its not always expected.
The simple solution is to just cache $(this) to a variable before drilling further into a chain:
$("li").each(function() {
// cache this
var list_item = $(this);
// get all child a tags
list_item.find("a").each(function() {
// scope of this now relates to a tags
$(this).hide("slow");
});
});
My two pain points have been the bracket hell, can get very confusing
$('.myDiv').append($('<ul />').append($('<li />').text('content')));
My other common issue has to do with the use of JSON in jQuery, I always miss the last comma,
$('.myDiv').tabs({ option1:true, options2:false(, woops)});
Finally, I've been using jQuery for about 6 months now and I don't think I'll ever go back to prototypes. I absolutely love jQuery, and a lot of the tricks they use have helped me learn a lot. one cool trick that I like is using string literals for method calls, I never really did that too much with prototypes.
$('.myDiv')[(add ? 'add' : 'remove') + 'Class']('redText');
(The only thing I can think of is that this is the element instead of a jQuery object in $("...").each(function)-calls, as $(element) is more often used then just the element. And that extremly minor thing is just about it.
Example of the above (simplified and I know that there are other much better ways to do this, I just couldn't think of a better example now):
// Make all divs that has foo=bar pink.
$("div").each(function(){
if($(this).attr("foo") == "bar"){
$(this).css("background", "pink");
}
});
each is a function that takes a function as parameter, that function is called once for each matching element. In the function passed, this refers to the actual browser DOM-element, but I find that you often will want to use some jQuery function on each element, thus having to use $(this). If this had been set to what $(this) is, you'd get shorter code, and you could still access the DOM element object using this.get(0). Now I see the reason for things being as they are, namely that writing $(this) instead of this, is hardly that cumbersome, and in case you can do what you want to do with the DOM element the way it is is faster than the way it could have been, and the other way wouldn't be faster in the case you want $(this).)
I don't think there are any real gotchas, or even any lingering annoyances. The other answers here seem to confirm this - issues are caused simply by the slightly different API and different JavaScript coding style that jQuery encourages.
I started using Prototype a couple of years ago and found it a revelation. So powerful, so elegant. After a few months I tried out jQuery and discovered what power and elegance really are. I don't remember any annoyances. Now I am back working on a project using Prototype and it feels like a step back (to be fair, we're using Prototype 1.5.1).
If you reversed the question - "What Prototype annoyances should I be aware of as a jQuery user?" - you would get a lot more answers.
Nope. Nada. Nyet.
.each:
jQuery (you need Index, even if you're not using it):
$.each(collection, function(index, item) {
item.hide();
});
Prototype (you're usually using the item, so you can omit the index):
collection.each(function(item) {
item.hide();
});
This is really only an annoyance if you're doing a lot of DOM manipulation. PrototypeJs automatically adds its API to DOM Elements, so this works in prototypejs (jQuery of course doesn't do this):
var el = document.createElement("div");
el.addClassName("hello"); // addClassName is a prototypejs method implemented on the native HTMLElement
Even without running the native element through the $() function.
PS: Should note that this doesn't work in IE.

Categories

Resources