Are classes indexed in modern browsers? - javascript

In past web development I have always tried to descend from an id when constructing a selector in javascript, previously in jQuery and more recently using document.querySelector()/.querySelectorAll(). This was for performance reasons. I was following advice in posts like this (http://www.artzstudio.com/2009/04/jquery-performance-rules/#descend-from-id).
For example:
$('#new-address-form input.address')
could be significantly faster than
$('.address')
Even when there was only one element on the page with a class of address. If there were a lot of different classes in the DOM, this could be a lot faster in some browsers (I'm looking at you IE<8).
But, experientially, it seems today that this is no longer the case. Can anyone point me to some documentation or code, for open-source browsers, that would confirm that modern browsers index elements by class?

The difference in performance is there, but it may not be significant unless you're working with a very large markup.
I setup a benchmark JS Fiddle that creates 20,000 div tags each nested 10 levels deep. Then attempts to select one at random based on class, and again based on ID with a descendant class.
I've benchmarked it here: https://jsfiddle.net/0wyLfnz8/14/
Results
Selecting by ID then descendant class was on average .018ms in Chrome
Selecting by ID then descendant class was on average 39.33ms in IE
Selecting by class alone was on average 12.178ms in Chrome
Selecting by class alone was on average 51.386ms in IE
Again, those results are in milliseconds with 500 tests over 20,000 HTML elements
Code for Benchmarking
$(document).ready(function() {
var d = new Date();
var st = d.getTime();
var start = st;
var et;
var max = 20000;
var numberOfTests = 500;
var elementDepth = 10;
for(var i = 1; i < max; i++) {
//lets make a random class too
r = Math.floor((Math.random() * 10) + 1);
var depth = "";
for(var j = 1; j<elementDepth;j++) {
depth += '<div class="depth' + j + '"></div>'
}
$('body').append('<div id="d'+i+'">'+depth+'<div class="depth'+elementDepth+'"><div class="c'+i+' r'+r+'">Hello, I am div number '+i+'</div></div></div>');
}
d = new Date();
var duration = d.getTime() - st;
console.log('Generating divs took ' + (duration/1000) + ' seconds');
idDuration = 0;
idTests = 0;
for(var i = 0; i < numberOfTests; i++) {
//choose a random div to select
r = Math.floor((Math.random() * max) + 1);
d = new Date();
st = d.getTime();
var tagbyID = $('#d'+r+ '.c'+r);
d = new Date();
et = d.getTime();
duration = et - st;
//console.log('Selecting by ID took ' + duration + ' milliseconds');
idDuration += duration;
idTests++;
}
console.log('---');
classDuration = 0;
classTests = 0;
for(var i = 0; i < numberOfTests; i++) {
//choose a random div to select
r = Math.floor((Math.random() * max) + 1);
d = new Date();
st = d.getTime();
var tagbyClass = $('.c'+r);
d = new Date();
et = et;
duration = d.getTime() - st;
//console.log('Selecting by class took ' + duration + ' milliseconds');
classDuration += duration;
classTests++;
}
console.log('---');
d = new Date();
console.log('total duration: ' + ((d.getTime() - start)/1000) + " seconds");
console.log('---');
console.log('Selecting by ID took '+idDuration+' milliseconds total over '+idTests+' tests.');
console.log('Selecting by class took '+classDuration+' milliseconds total over '+classTests+' tests.');
console.log('---');
console.log('Average time for ID selection using $(\'#parentID .childClassName\') was: ' + (idDuration / idTests)+" milliseconds")
console.log('Average time for class selection using $(\'.className\') was: ' + (classDuration / classTests)+" milliseconds")
})

I may have answered my own question. I created a jsperf, but I would value more test data or improvements.
http://jsperf.com/should-we-always-descend-by-an-id/6
EDIT: In my test case it looks like jQuery is actually faster when you DON'T descend from an id, but the clear performance winner (cross-browser) is document.getElementsByClassName().

Related

Display two images every X hours, Javascript

Below is a code I am running to display a pair of images each day. I have been unable to have it display the images randomly, but instead each day it selects the next image in the Array (not at random). Javascript is not my specialty and I have been unsuccessful in making it select a new random image in both arrays each day.
I have also been unsuccessful in making it select them every 12 hours instead of each day, which is what I would prefer it to do. It must display the same ones for every person who views it for that period, until the timer resets, if possible.
<script type="text/javascript"><!--
var imlocation = "https://s31.postimg.org/";
function ImageArray (n) {
this.length = n;
for (var i =1; i <= n; i++) {
this[i] = ' '
}
}
function linkArray (n) {
this.length = n;
for (var i =1; i <= n; i++) {
this[i] = ' '
}
}
image = new ImageArray(4);
image[0] = 'v85buoebv/Day2.png';
image[1] = 'djdl322kr/Evening2.png';
image[2] = 'arubcg423/Night2.png';
image[3] = 'xf9kiljm3/Morning2.png';
link = new linkArray(11);
link[0] = 'q4xda5xdn/CLOUDY.png';
link[1] = '7141ttkjf/Heavyrain.png';
link[2] = 'gzp0gatyz/lightrain.png';
link[3] = 'xc3njrxob/Medium_Rain.png';
link[4] = 'x0m770h8b/NO_WEATHER.png';
link[5] = 's38mlwf97/omgrain.png';
link[6] = 'btigj04l7/Special_Weather.png';
link[7] = 'b59m025vf/WEREALLGONNADIErain.png';
link[8] = 'ubmt38md7/Windy.png';
link[9] = 'x0m770h8b/NO_WEATHER.png';
link[10] = 'x0m770h8b/NO_WEATHER.png';
var currentdate = new Date();
var imagenumber = currentdate.getDay();
document.write('<div id="NOTICEME"><center><img src="' + imlocation + image[imagenumber] + '"><img src="' + imlocation + link[imagenumber] + '"></center><br><div id="mowords">[center][img]' + imlocation + image[imagenumber] + '[/img][img]' + imlocation + link[imagenumber] + '[/img][/center]</div></div>');
//--></script>
I used this code as a base to go on. But no matter how I toy with it, it won't give me a random image from the array. Also, the div ID "mowords" and "NOTICEME" is an ID I am using for CSS reasons that has nothing to do with this code.
Edit:
Maybe going for random is the wrong way to do this. Is there a way to make the link = new linkArray select the date (as in 1 - 31) and the image = new ImageArray select the day (as in 1 - 7) like it is currently doing? It will create variance and the illusion of randomness in the long run.
If you know your arrays' indices, and they are integers, then you can use the following to get a pseudo-random integer between min and max:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
Now, however, your approach has been giving you certain "consistency" in your results, i.e. each day of the week giving you a certain image on every page show.
If you wish to implement randomness into it, you won't get such consistent results, meaning that on every page show, the image will be random independent of the day of the week or time of day.
To address this issue, you can take several approaches:
Have a server-side scripting language define the images (random or not) and save the "daily"/12-hour preferences into a .json/.js file, which then can be read by the JavaScript running in the browser. With this approach you would probably set the "refresh" rate via adding Expires headers on the .js file handling the parsing of the configuration file created by your server-side script -> https://gtmetrix.com/add-expires-headers.html
The other approach is to redefine your image selection logic based on the current date/time. However, the obvious downfall to that, is that you rely on the date and time of the user's computer, which can't always be trusted, so you have to work around that.
I would advise to look into a server-side scripting solution - PHP/Perl would do fine for this purpose.
UPDATED:
Have not tested, but try this (as per your comments):
<script type="text/javascript"><!--
var imlocation = "https://s31.postimg.org/";
function ImageArray (n) {
this.length = n;
for (var i = 0; i <= n; i++) {
this[i] = ''
}
}
function linkArray (n) {
this.length = n;
for (var i = 0; i <= n; i++) {
this[i] = ''
}
}
image = new ImageArray(6);
image[0] = 'v85buoebv/Day2.png';
image[1] = 'djdl322kr/Evening2.png';
image[2] = 'arubcg423/Night2.png';
image[3] = 'xf9kiljm3/Morning2.png';
image[4] = '';
image[5] = '';
image[6] = '';
link = new linkArray(30);
link[0] = 'q4xda5xdn/CLOUDY.png';
link[1] = '7141ttkjf/Heavyrain.png';
link[2] = 'gzp0gatyz/lightrain.png';
link[3] = 'xc3njrxob/Medium_Rain.png';
link[4] = 'x0m770h8b/NO_WEATHER.png';
link[5] = 's38mlwf97/omgrain.png';
link[6] = 'btigj04l7/Special_Weather.png';
link[7] = 'b59m025vf/WEREALLGONNADIErain.png';
link[8] = 'ubmt38md7/Windy.png';
link[9] = 'x0m770h8b/NO_WEATHER.png';
link[10] = 'x0m770h8b/NO_WEATHER.png';
link[11] = '';
link[12] = '';
link[13] = '';
link[14] = '';
link[15] = '';
link[16] = '';
link[17] = '';
link[18] = '';
link[19] = '';
link[20] = '';
link[21] = '';
link[22] = '';
link[23] = '';
link[24] = '';
link[25] = '';
link[26] = '';
link[27] = '';
link[28] = '';
link[29] = '';
link[30] = '';
var currentdate = new Date();
var dM = currentdate.getDate() - 1;
var dW = currentdate.getDay();
//var imagenumber = currentdate.getDay();
document.write('<div id="NOTICEME"><center><img src="' + imlocation + image[dW] + '"><img src="' + imlocation + link[dM] + '"></center><br><div id="mowords">[center][img]' + imlocation + image[dW] + '[/img][img]' + imlocation + link[dM] + '[/img][/center]</div></div>');
//--></script>
You use the function getDay() on the currentDate, therefore it will not change throughout a single day. Have a look at http://www.w3schools.com/jsref/jsref_getday.asp where you notice that its an integer from 0 to 6, and the week starts on sunday.
Therefore today (wednesday august 03 is 3) you should see image[3] and link[3]. Tomorrow you should get an error because you will reference outside of the image array i.e. image[4] will throw an index out of bounds exception.
Well, here goes my first answer here ever!
JavaScript is client-side code, so if the JavaScript code is what is determining the random number (rather than server-side code), you can't guarantee everyone accessing the site sees the same thing.
You could, however, use the date and time as a seed to generate what could appear random:
var btn = document.getElementById('btn');
btn.onclick = function () {
var d = new Date();
// getHours() results in an integer 0-23
var h = d.getUTCHours();
var chooser;
//Pick a multiplier based on the time of day.
if (h < 12) {
chooser = 1.15;
} else {
chooser = 1.87;
}
//Change upperLimit to whatever the upper limit of your array may end up being.
var upperLimit = 10;
//Generate the result
var pseudoRand = parseInt((d.getUTCFullYear() + d.getUTCMonth() + d.getUTCDay()) * chooser % upperLimit);
btn.textContent = pseudoRand;
}
Fiddle: https://jsfiddle.net/tapjzg94/
That code replaces the text of the button with an integer from 0 to upperLimit that should be the same for everyone who clicks on it, avoiding time zone issues with the UTC versions of the Date functions.
You could mix and match the date functions however you want, so long as they are all numbers that don't change on a rapid basis (year/month/date/day vs. minutes/seconds/milliseconds).

Add rows values of a time

I have set of three times in the format of Minute:Seconds:Miliseconds which I need to add to together and get the total time..
for example the one I have used is : 0:31.110 + 0:50.490 + 0:32.797
which = 1:54.397
so how to do this in javascript?
Here is JS Code
var sp1 = $('#table tr td:nth-child(2)').text()
var sp2 = $('#table tr td:nth-child(3)').text()
var sp3 = $('#table tr td:nth-child(4)').text()
var1 = sp1 + sp2 + sp3
$('td:nth-child(5)').html(var1);
I don't know where to begin but I have just come up with the above code..
I need the output to be 1:54.397 in the last td, but I get this 0:31.1100:50.4900:32.797 shown in this example http://jsfiddle.net/q1kumbea/
You may use moment.js for this. That would make it very easy, as you can just parse the times in the correct format, add the moments together ...
var sp1 = $('#table tr td:nth-child(2)').text()
var sp2 = $('#table tr td:nth-child(3)').text()
var sp3 = $('#table tr td:nth-child(4)').text()
var1 = moment(sp1, "mm:ss.SSS") + moment(sp2, "mm:ss.SSS") + moment(sp3, "mm:ss.SSS")
$('td:nth-child(5)').html(moment(var1).format("mm:ss.SSS"));
... and voila
Updated fiddle
I don't know any native functionality, but you can always(almost:) ) use some maths to achieve what you want. like below
var plusTimes = function(arr) {
var resultTime =0;
for(var i = 0; i < arr.length; i ++) {
resultTime += (parseInt((arr[i].split(':')[0]) * 60 * 1000) + ( parseInt(arr[i].split(':')[1].split('.')[0]) * 1000 ) + parseInt(arr[i].split('.')[1]))
}
var ms = (resultTime / 1000).toString().split('.')[1];
var sec = parseInt((resultTime / 1000).toString().split('.')[0]) % 60;
var min = (parseInt((114397 / 1000).toString().split('.')[0]) - parseInt((114397 / 1000).toString().split('.')[0]) % 60) / 60;
return min + ':' + sec + '.' + ms;
}
plusTimes(['0:31.110','0:50.490', '0:32.797']) // outputs "1:54.397"
you can add as many numbers as you wish to array unless you keep their format the same

Fastest bulk insert into javascript arrays

I wanted to check to see which was faster in adding elements to an array in javascript:
Adding a list of 5 per iteration
Adding 5 single items with adding position to the subscript
Adding one single item pr iteration
I ran it on my Linux Mint 16, Firefox 37.0.2
1 and 2 turned out much better than 3.
When I ran it 1,000,000 times 1 was noticably better than 2.
However when I ran it 10,000,000 the results were reversed. What would be the explanation for that?
var amount = 1000000;
var iter = 11;
var a = new Array(amount);
var b = new Array(amount);
var results = [];
for (j=1; j<iter; j++) {
var clock = new Date().getTime();
for (i=0; i< amount; i+=5) {
a[i] = [2,2,2,2,2];
}
results.push("quintuple primitive insert attempt " + j + " took " +
eval(new Date().getTime() - clock) + "ms");
var clock = new Date().getTime();
for (i=0; i< amount; i+=5) {
a[i] = 2;
a[i+1] = 2;
a[i+2] = 2;
a[i+3] = 2;
a[i+4] = 2;
}
results.push("single primitive insert with inline inc attempt " + j +
" took " + eval(new Date().getTime() - clock) + "ms");
var clock = new Date().getTime();
for (i=0; i< amount; i++) {
a[i] = 2;
}
results.push("single primitive insert with single iterator attempt " +
j + " took " + eval(new Date().getTime() - clock) + "ms");
}
The code is demonstrated here:
http://jsfiddle.net/lash/cL3wewj4/
(I also tried using homogenous and heterogenous arrays to insert content, in which 2 always was the best. The attempt is in the same jsfiddle source, commented out)
You're taking times of things that are not the same:
First off, you're taking times of creating a matrix whose elements at position i%5 = 0 contain an array of five elements. The rest of the elements (i%5 != 0) are undefined. This array has a length that depends on amount due to the fact that you're adding to the variable i five in each iteration and some elements at the end of the array might not be initialized (directly or indirectly).
for (i = 0; i < amount; i += 5) {
a[i] = [2, 2, 2, 2, 2];
}
Secondly, you're creating an array whose elements are all equal to 2.
for (i = 0; i < amount; i += 5) {
a[i] = 2;
a[i + 1] = 2;
a[i + 2] = 2;
a[i + 3] = 2;
a[i + 4] = 2;
}
Thirdly, you're doing the same as two.
for (i=0; i< amount; i++) {
a[i] = 2;
}
Since one is different than 2 and 3 the results don't make sense.

How to make js "for" loop more efficient?

I am wondering how to make the loop below more efficient. On my page it has more iterations than 100, that is why, as you can imagine it is troublesome.
for (var i = 0; i < 1000; i += 1) {
var el = document.createElement('div');
el.appendChild(document.createTextNode('Node ' + (i + 1)));
document.getElementById('nodeHolder').appendChild(el);
}
Thanx for help in advance.
All I can really suggest is getting a reference to the nodeHolder element outside of the for:
var nodeHolder = document.getElementById('nodeHolder');
for (var i = 0; i < 1000; i += 1) {
var el = document.createElement('div');
el.appendChild(document.createTextNode('Node ' + (i + 1)));
nodeHolder.appendChild(el);
}
Or perhaps using a document fragment to hold the interim changes/appends and then add that to to the nodeHolder after the loop:
var fragment = document.createDocumentFragment();
for (var i = 0; i < 1000; i += 1) {
var el = document.createElement('div');
el.appendChild(document.createTextNode('Node ' + (i + 1)));
fragment.appendChild(el);
}
document.getElementById('nodeHolder').appendChild(fragment.cloneNode(true));
JS Fiddle showing both approaches (with timing if the console is available).
jQuery way...
var d = '';
for(var i=0;i<1000;i++) d += '<div>Node ' + (i + 1) + '</div>';
$('#nodeHolder').append(d);
Or javascript inside html...
<div id="nodeHolder">
<script>(function() { for(var i=0;i<1000;i++) document.write('<div>Node ' + (i + 1) + '</div>'); })();</script>
</div>
Maybe: You could generate a HTML String in the for loop like:
<div>Node 1</div><div>Node 2</div>
and then set the .innerHTML property of nodeHolder to this string after the whole loop is completed.
(... and let the DOM renderer figure out how to best optimize the action)
In summary:
Cache your DOM selector.
Ditch the for loop, and go for a reverse while loop.
Only append your element to the DOM once. The DOM is almost always the bottleneck.
In this pattern, you can take advantage of a reverse loop:
//Cache the DOM element
var node = document.getElementById('nodeHolder'),
markup = [];
//Run the loop backwards for additional speed
var i = 10000;
while(i--){
markup.push('<div>Node ' + (i + 1) + '</div>');
}
//Reverse the array, join it, then set the innerHTML
node.innerHTML = markup.reverse().join('');​
Working example: http://jsfiddle.net/ZAkMZ/3/
Reverse while loop speed reference: https://blogs.oracle.com/greimer/entry/best_way_to_code_a
jQuery version:
//Cache the DOM element
var node = $('#nodeHolder'),
markup = [];
//Run the loop backwards for additional speed
var i = 10000;
while(i--){
markup.push('<div>Node ' + (i + 1) + '</div>');
}
//Reverse the array, join it, then set the innerHTML
node.append(markup.reverse().join(''));​
You definitely need to use DocumentFragment as suggested by #David Thomas.
The most efficient version as I see is this... cloneNode is always better than createElement, switching between shallow or deep copy (still faster than createEl) as required.
Not that it gives a significant gain but you should shy away from assignments when you can. Store data in variables only when you need to. Of course when it comes to readability it's a different thing.
var fragment = document.createDocumentFragment();
var tplEl = document.createElement('div');
var contentTpl = document.createTextNode('Node ');
for (var i = 1; i <= 1000; i++) {
var curNode = contentTpl.cloneNode(false);
curNode.nodeValue = curNode.nodeValue + i;
tplEl.cloneNode(false).appendChild(curNode);
fragment.appendChild(tplEl);
}
document.getElementById('nodeHolder').appendChild(fragment);
Note that adding the fragment to the nodeHolder adds all childs of fragment as childs to nodeHolder triggering only 1 flow as opposed to 1000 flows in your earlier code.
Reference: Speeding up JavaScript
May be this way:
for (var i = 0; i < 100; i+=1) {
$("<div/>").appendTo("body").attr({"class":'txtHolder'});
$(".txtHolder").append(i+1);
}​

(P)RNG - Array of Random Numbers Created with a Seed

I want to create an array of random/pseudo-random numbers using a seed. I want the very same array to be created when the same seed is used and I want to have little or no visible pattern in the array. I'm working in JavaScript.
This is the random function I'm using, which I'm quite happy with (sorry, I forgot who the original author is):
function random(seed) {
if (!seed) seed = new Date().getTime();
seed = (seed*9301+49297) % 233280;
return seed/(233280.0);
}
This is the array generation:
var superSeed = random();
var nRandom = 100;
var randomArray = new Array();
for (var i = 0 ; i < nRandom ; i++){
randomArray.push(random((superSeed*10)+ (i)));
}
Somehow the pattern seems to be quite similar, no matter how often I run it. This question seems to be similar, but since it's about matrixes, I don't understand what's being said.
Thanks!
Having worked on similar things before I think we can use a fairly simple series, which takes two initial values and then you can get a lot more.
var a1,b1;
function InitSequence(v1, v2) {
a1 = Math.pow(v1, 5) / Math.pow(v1, 3);
b1 = Math.pow(v2, 8);
lastrand = (a1 + b1) & 0x7fffffff;
}
function SequenceNext() {
var alast = a1;
var nextVal = (a1 + b1) & 0x7fffffff;
b1 = alast;
a1 = nextVal;
return nextVal;
}
Then use it like this:
InitSequence(75, 21);
for (var i = 0; i < 99; i++) {
v = SequenceNext();
}
I tested it like this:
var used = new Array();
InitSequence(75, 21); // any two values will do.
// fill 10k into array.
for (var i = 0; i < 9999; i++) {
v = SequenceNext();
if (undefined != used[v]) {
used[v]++;
} else used[v] = 1;
//document.write(i+": "+v+"<br>");
}
// see if there any duplicates.
var tdup = 0;
for (xx in used) {
ndup = used[xx];
if (ndup > 1) {
document.write("duplicated " + xx + " :" + ndup + "<br>");
tdup += ndup;
}
}
document.write("Total dups " + tdup + "<br>");
This is using the Fibonacci series, which in mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation
. I'm starting with different values - (v1^5) / (v1^3) and v2 ^ 8; otherwise it would only ever be identical.
I like the "Super 7" PRNG. It is simple, fast (although the other answer with the fib. sequence is fast as well), and has the interesting property:
In the entire range -- albeit of a meager of 32k -- there are no duplicates using the "Super 7" PRNG.
Multiple 7's can be joined to increase the number of bits and/or provide multiple seeds. This non-duplication property can exposed or folded.
(The sequence of a PRNG is always the same given a starting seed: it's the distribution and cycle lengths that are interesting -- it is these properties that may make them ideal in different cases where "true randomness" isn't desired).
Happy coding.
Maybe you should try this
function s_random() {
s_random.m = 71402523; s_random.a = 409647; s_random.c = 1508892;
s_random.seed = (s_random.seed*s_random.a + s_random.c) % s_random.m;
return s_random.seed / s_random.m;
}
/*
generate IV
s_random.seed = Math.floor((new Date).getTime()/10000);
*/
s_random.seed = 130324232;
var CheckRandom = 4999999;
var PrintSamples = 100;
var used = new Array();
for (var i = 0; i < CheckRandom; i++) {
v = (Math.ceil(Math.sqrt(s_random())* 1000000000) * 8);
if (undefined != used[v]) {
used[v]++;
} else used[v] = 1;
if ( i< PrintSamples) document.write(i+": "+v+"");
}
/* see if there are any duplicates. */
var tdup = 0;
for (xx in used) {
ndup = used[xx];
if (ndup > 1) {
if (ndup < PrintSamples) document.write("duplicated " + xx + " :" + ndup + "");
tdup += ndup;
}
}
document.write("Total generated " + CheckRandom + "");
document.write("Total duplicates " + tdup + "");
Just got 5 million seeded, repeatable random numbers and no duplicates. Tested several times on Mac OS X with Safari.
Cheers,
Karl

Categories

Resources