Ways to improve performance of this javascript code? - javascript

Don't be frightened, its a very basic code.
Just wanted to check with you guys if you know any methods to make it faster ? (still learning)
It looks so ugly :)
Or, if you notice anything which could be made differently... Thanks!
function pic_form_function(form, nr, val) {
document.getElementById("image_to_delete").value=0;
var re = /\..+$/;
var extension = val.match(re);
if (extension==".jpg" || extension==".jpeg" || extension==".gif" || extension==".png" || extension==".bmp") {
if (nr==1){
var ID_file1 = document.getElementById("pic_id_nr1").value;
window.parent.document.getElementById("pic1_filename").value=ID_file1+extension;
window.parent.document.getElementById("annonsera_nr_pics").value=1;
window.parent.document.getElementById("iframe_upload_pic").style.height="180px";
document.getElementById("pic_target").style.height="160px";
document.getElementById("pic_target").style.display="block";
document.getElementById("remove_pic").value=0;
document.getElementById("extra_pic_checkbox").style.display="inline";
document.getElementById("extra_pic_fnt").style.display="inline";
}
else if (nr==2){
var ID_file2 = document.getElementById("pic_id_nr2").value;
window.parent.document.getElementById("pic2_filename").value=ID_file2+extension; //Passing fileInputName to parent window...
window.parent.document.getElementById("annonsera_nr_pics").value=2;
document.getElementById("extrapic").value=2;
document.getElementById("pic_file3").disabled=false;
}
else if (nr==3){
var ID_file3 = document.getElementById("pic_id_nr3").value;
window.parent.document.getElementById("annonsera_nr_pics").value=3;
window.parent.document.getElementById("pic3_filename").value=ID_file3+extension;
document.getElementById("extrapic").value=3;
document.getElementById("pic_file4").disabled=false;
}
else if (nr==4){
var ID_file4 = document.getElementById("pic_id_nr4").value;
window.parent.document.getElementById("annonsera_nr_pics").value=4;
window.parent.document.getElementById("pic4_filename").value=ID_file4+extension;
document.getElementById("extrapic").value=4;
document.getElementById("pic_file5").disabled=false;
}
else if (nr==5){
var ID_file5 = document.getElementById("pic_id_nr5").value;
window.parent.document.getElementById("annonsera_nr_pics").value=5;
window.parent.document.getElementById("pic5_filename").value=ID_file5+extension;
document.getElementById("extrapic").value=5;
}
}
if (extension!=".jpg" && extension!=".jpeg" && extension!=".gif" && extension!=".png" && extension!=".bmp") {
window.parent.document.getElementById("annonsera_imagenotvalid_error").style.display="block";
}
else {
window.parent.document.getElementById("annonsera_imagenotvalid_error").style.display="none";
form.submit();
}
}

If me, I will define following on the top.
$=function(x){return document.getElementById(x);}
will replace all the document.getElementById to $ first.
or better user jQuery.

About performance:
To extract the file extension you can use the String.substring method instead of a RegExp, the performance improvement would be negligible but I think it gains readability:
var extension = val.substring(val.lastIndexOf('.'));
About the code:
You could have only one ID_file variable declared at the top of your function, and use it in the if blocks.
The else if blocks where nr==2, 3, and 4 are really similar, and you could do the same for those three cases:
//..
else if (nr >= 2 && nr <= 4){
ID_file = document.getElementById("pic_id_nr"+nr).value; // point #1 assumed
window.parent.document.getElementById("pic"+nr+"_filename").value=ID_file+extension;
window.parent.document.getElementById("annonsera_nr_pics").value = nr;
document.getElementById("extrapic").value = nr;
document.getElementById("pic_file"+(+nr+1)).disabled=false;
}
About readability:
You could define shorthands to common and verbose function calls at the beginning as S.Mark also suggests:
var el = document.getElementById,
parentEl = window.parent.document.getElementById;

Continuing what CMS did with code repetition, you can refactor the common code outside the sequence of else if blocks.
The switch statement was made to replace a sequence of ifs.
Instead of the above two suggestions, you could define functions do to the same tasks for a more readable implementation.
If you continue to use regexps (I personally find them very readable), remember that match returns an array.
Also, the .+ will greedily match all characters after the first period. Better to only match non-periods with [^.]+.
Instead of the long sequence of string comparisons, you can use objects as associative arrays:
var imageExtensions = {'.jpg': 1, '.jpeg': 1, '.gif': 1, '.png': 1, '.bmp': 1};
if (imageExtensions[extension]) {
The last if ... else ... is unnecessary, considering that the condition checks for the negation of the condition in the first if. Just move the else block to the end of the first if and turn the last if (...) to an else.
Personally, I find short error handling code more readable when placed next to the if statement that detected the error, rather than after a long block handling the non-error case. After the previous refactor, let's swap the if block with the else block and negate the conditional.
Taking all the above together, we get:
function pic_form_function(form, nr, val) {
document.getElementById("image_to_delete").value=0;
var extension = val.match(/\.[^.]+$/);
if (extension) {
extension = extension[0];
}
var imageExtensions = {'.jpg': 1, '.jpeg': 1, '.gif': 1, '.png': 1, '.bmp': 1};
if (! imageExtensions[extension]) {
window.parent.document.getElementById("annonsera_imagenotvalid_error").style.display="block";
} else {
var ID_file = document.getElementById("pic_id_nr" + nr).value;
window.parent.document.getElementById("pic"+nr+"_filename").value=ID_file+extension;
window.parent.document.getElementById("annonsera_nr_pics").value=nr;
switch (nr) {
case 1:
window.parent.document.getElementById("iframe_upload_pic").style.height="180px";
document.getElementById("pic_target").style.height="160px";
document.getElementById("pic_target").style.display="block";
document.getElementById("remove_pic").value=0;
document.getElementById("extra_pic_checkbox").style.display="inline";
document.getElementById("extra_pic_fnt").style.display="inline";
break;
case 2: // FALLTHRU
case 3: // FALLTHRU
case 4:
document.getElementById("pic_file"+nr).disabled=false;
// FALLTHRU
case 5:
document.getElementById("extrapic").value=nr;
break;
}
window.parent.document.getElementById("annonsera_imagenotvalid_error").style.display="none";
form.submit();
}
}
which is shorter, but could be more readable.
Note that if the $ wrapper is defined in 'window.parent', you should be able to call it as window.parent.$ in the child:
window.parent.$("annonsera_imagenotvalid_error").style.display="none";

There isn't any special computation being done beside traversing elements and assigning their attributes some values. So the code is not performing much to try to improve its performance.
On the other hand, you should use a library like jQuery for the kind of work you are doing. You might be able to cut short many traversals because you can have jQuery reuse the object it found and continue further from this point to look for other objects...

You should cache your dom lookups e.g.
var pic_target = document.getElementById("pic_target");
You can then use this variable to to apply the style and it only does the dom lookup once. I think this is a key thing to speeding up the js

Related

What is quicker in nodeJs to process, else if statements or avoiding them with return?

What is quicker to process in javascript or NodeJS, else if statements or avoiding them with returns?
Case A
function test(x) {
if (x < 2000) {
return 'output1'
} else if (x > 3450) {
return 'output2'
} else {
return 'output3'
}
}
Case B
function test(x) {
if (x < 2000) {
return 'output1'
}
if (x > 3450) {
return 'output2'
}
return 'output3'
}
I have a gut feeling that case B is quicker with better performance, but just want to confirm with your feedback. Yes, I'm aware the difference is tiny and it would be merely a micro-optimization, but what is faster, case A or B?
An else is always implied when you have an if (because the compiler should know where to jump when the condition is not met. This "where" part is the implied else). There are no scopes in the produced compiled code. There are only instructions one after another. Some of them are labeled so they can be the target of a jump.
I am pretty sure that the output of both code samples is exactly the same. In pseudoassembly it would be something like
[check-equal] $x, 12142 // check equality
[goto-if-zero] :label1 // Conditional jump to label1 position in case previous check was false
[set-retval] 'output1' // setting return value
[goto] :end // unconditional jump to the end of funtion
label1: [check-equal] $x, 798789 // check equality
[goto-if-zero] :label2 // Conditional jump to label2 position in case previous check was false
[set-retval] 'output2' // setting return value
[goto] end // unconditional jump to the end of funtion
label2: [setretval] 'output3' // setting return value
end: [return] // returing
Thank you guys for citing me the Javascript benchmark which I was not aware of. Well, I made some tests and contrary to my gut feeling, case A is slightly faster or I can say that the difference is neglectable because the difference is within the error margins.
test suites here:
https://jsbench.me/mqka3oap2y/1
https://jsperf.com/else-if-or-avoid-with-return/1
using this code
for (var i=0; i<10000; i++) {
test(i)
}
Case A: 140,931 ops/s ±1.03%
Case B: 140,147 ops/s ±1.12%

How to reduce "if statement" conditions? [reduce the conditions inside the if statement]

after days of hard thinking i choose to ask that question. I have if statement with multiple conditions:
//var current is array of arrays of integers
if((current[rot][0] + x)<blocks.length
&& (current[rot][1] + x)<blocks.length
&& (current[rot][2] + x)<blocks.length
&& (current[rot][3] + x)<blocks.length
&& !$(blocks[current[rot][0]+x]).hasClass("blockLand")
&& !$(blocks[current[rot][1]+x]).hasClass("blockLand")
&& !$(blocks[current[rot][2]+x]).hasClass("blockLand")
&& !$(blocks[current[rot][3]+x]).hasClass("blockLand"))
{
//something to happen here ONCE!
}
Because i want something inside to happen just once i think i cant use for loop.
So my question is: is there a possible way to reduce the conditions number? and how?
P.S.: Yes i figured out that i can use flag (true/false) inside and do my stuff outside this if, in another if - but i think that not always gonna work, because for every loop the flag will be different.
var b = true;
for (var i = 0; i <= 3; i++) {
// In two lines for being clear, but it's possible just in one
b = b && (current[rot][i] + x)<blocks.length
b = b && !$(blocks[current[rot][i]+x]).hasClass("blockLand");
// You could speed it up this way.
if(!b) break;
}
if (b) {
//something to happen here ONCE!
}
I think I understand what you are asking but let me know if there is anything else I can do.
JavaScript has a ternary (conditional operator) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
This operator allows you to assign true/false values based on an internal if/else condition.
Here is some code for you to explain this...
window.onload = function() {
var one = 1;
var two = 2;
console.log(one > two ? "greater" : "not greater");
};
You can also use a Switch statement which you can read about here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch.
Here is an example of a switch statement.
window.onload = function() {
var string = "testing this out";
switch (string) {
case "testing this out":
console.log('testing this out found in condition one');
break;
case "testing":
console.log('found testing');
break;
default:
console.log('not found');
break;
}
};
Let me know if I can improve this.

Better method of checking a bunch of conditions

I'm new to javascript and still coming to terms with the language's nuances.
I have a piece of code where I have to check a set of conditions on a particular variable.
if (a=="MAIN_DOMAINNAME" || a=="DOMAIN_SERIAL" || a=="DOMAIN_REFRESH" || a=="DOMAIN_RETRY" || a=="DOMAIN_EXPIRE" || a=="DOMAIN_NEGTTL" || a=="MAIN_NS") {
Is there a better way to do this conditional check, like say:
if a is one of ("DOMAIN_SERIAL", "MAIN_DOMAINNAME", "DOMAIN_REFRESH" ) {?
Assuming a relatively modern browser, you can use Array.indexOf (spec)
if (["DOMAIN_SERIAL", "MAIN_DOMAINNAME", "DOMAIN_REFRESH"].indexOf(a) !== -1)
Note - you can easily shim it for older browsers (see the mdn link on how).
A regex would be shorter and works everywhere :
if ( /^(MAIN_DOMAINNAME|DOMAIN_SERIAL|DOMAIN_REFRESH|..)$/.test(a) ) {
// do stuff
}
FIDDLE
var ars = ["DOMAIN_SERIAL", "MAIN_DOMAINNAME", "DOMAIN_REFRESH"];
if(ars.some(function(ar){ return a === ar; })){
// do smth
}
Should mention the switch statement as it should be working fine with the example given in the question.
switch(a) {
case('MAIN_DOMAINAME'):
case('DOMAIN_SERIAL'):
case('DOMAIN_REFRESH'):
case('DOMAIN_RETRY'):
console.log('Go wild.');
break;
}
Not as lightweight as the other answers, but it's readable and matches (a === b).
I prefer the regex solution already provided by adeneo, but if you want something that matches the
if a is one of (...
wording from the question reasonably closely you can do this:
if (a in list("MAIN_DOMAINNAME", "DOMAIN_SERIAL", "DOMAIN_REFRESH", "DOMAIN_RETRY")) {
// do something (rest of list omitted to avoid scrolling)
}
by providing a helper function to turn the list into an object:
function list() {
var o={}, i;
for (i=0; i < arguments.length; i++) o[arguments[i]] = true;
return o;
}
Of course you can omit the helper function and just use an object literal, but that's ugly:
if (a in {"MAIN_DOMAINNAME":1, "DOMAIN_SERIAL":1, "DOMAIN_REFRESH":1}) {

alternatives for excessive for() looping in javascript

Situation
I'm currently writing a javascript widget that displays a random quote into a html element. the quotes are stored in a javascript array as well as how many times they've been displayed into the html element. A quote to be displayed cannot be the same quote as was previously displayed. Furthermore the chance for a quote to be selected is based on it's previous occurences in the html element. ( less occurrences should result in a higher chance compared to the other quotes to be selected for display.
Current solution
I've currently made it work ( with my severely lacking javascript knowledge ) by using a lot of looping through various arrays. while this currently works ( !! ) I find this solution rather expensive for what I want to achieve.
What I'm looking for
Alternative methods of removing an array element from an array, currently looping through the entire array to find the element I want removed and copy all other elements into a new array
Alternative method of calculating and selecting a element from an array based on it's occurence
Anything else you notice I should / could do different while still enforcing the stated business rules under Situation
The Code
var quoteElement = $("div#Quotes > q"),
quotes = [[" AAAAAAAAAAAA ", 1],
[" BBBBBBBBBBBB ", 1],
[" CCCCCCCCCCCC ", 1],
[" DDDDDDDDDDDD ", 1]],
fadeTimer = 600,
displayNewQuote = function () {
var currentQuote = quoteElement.text();
var eligibleQuotes = new Array();
var exclusionFound = false;
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i];
if (exclusionFound === false) {
if (currentQuote == iteratedQuote[0].toString())
exclusionFound = true;
else
eligibleQuotes.push(iteratedQuote);
} else
eligibleQuotes.push(iteratedQuote);
}
eligibleQuotes.sort( function (current, next) {
return current[1] - next[1];
} );
var calculatePoint = eligibleQuotes[0][1];
var occurenceRelation = new Array();
var relationSum = 0;
for (var i = 0; i < eligibleQuotes.length; i++) {
if (i == 0)
occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
else
occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
relationSum = relationSum + (occurenceRelation[i] * 100);
}
var generatedNumber = Math.floor(relationSum * Math.random());
var newQuote;
for (var i = 0; i < occurenceRelation.length; i++) {
if (occurenceRelation[i] <= generatedNumber) {
newQuote = eligibleQuotes[i][0].toString();
i = occurenceRelation.length;
}
}
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i][0].toString();
if (iteratedQuote == newQuote) {
quotes[i][1]++;
i = quotes.length;
}
}
quoteElement.stop(true, true)
.fadeOut(fadeTimer);
setTimeout( function () {
quoteElement.html(newQuote)
.fadeIn(fadeTimer);
}, fadeTimer);
}
if (quotes.length > 1)
setInterval(displayNewQuote, 10000);
Alternatives considered
Always chose the array element with the lowest occurence.
Decided against this as this would / could possibly reveal a too obvious pattern in the animation
combine several for loops to reduce the workload
Decided against this as this would make the code to esoteric, I'd probably wouldn't understand the code anymore next week
jsFiddle reference
http://jsfiddle.net/P5rk3/
Update
Rewrote my function with the techniques mentioned, while I fear that these techniques still loop through the entire array to find it's requirements, at least my code looks cleaner : )
References used after reading the answers here:
http://www.tutorialspoint.com/javascript/array_map.htm
http://www.tutorialspoint.com/javascript/array_filter.htm
http://api.jquery.com/jQuery.each/
I suggest array functions that are mostly supported (and easily added if not):
[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});
Other useful functions include forEach and map.
I agree that combining all the work into one giant loop is ugly (and not always possible), and you gain little by doing it, so readability is definitely the winner. Although you shouldn't need too many loops with these array functions.
The answer that you want:
Create an integer array that stores the number of uses of every quote. Also, a global variable Tot with the total number of quotes already used (i.e., the sum of that integer array). Find also Mean, as Tot / number of quotes.
Chose a random number between 0 and Tot - 1.
For each quote, add Mean * 2 - the number of uses(*1). When you get that that value has exceeded the random number generated, select that quote.
In case that quote is the one currently displayed, either select the next or the previous quote or just repeat the process.
The real answer:
Use a random quote, at the very maximum repeat if the quote is duplicated. The data usages are going to be lost when the user reloads/leaves the page. And, no matter how cleverly have you chosen them, most users do not care.
(*1) Check for limits, i.e. that the first or last quota will be eligible with this formula.
Alternative methods of removing an array element from an array
With ES5's Array.filter() method:
Array.prototype.without = function(v) {
return this.filter(function(x) {
return v !== x;
});
};
given an array a, a.without(v) will return a copy of a without the element v in it.
less occurrences should result in a higher chance compared to the other quotes to be selected for display
You shouldn't mess with chance - as my mathematician other-half says, "chance doesn't have a memory".
What you're suggesting is akin to the idea that numbers in the lottery that haven't come up yet must be "overdue" and therefore more likely to appear. It simply isn't true.
You can write functions that explicitly define what you're trying to do with the loop.
Your first loop is a filter.
Your second loop is a map + some side effect.
I don't know about the other loops, they're weird :P
A filter is something like:
function filter(array, condition) {
var i = 0, new_array = [];
for (; i < array.length; i += 1) {
if (condition(array[i], i)) {
new_array.push(array[i]);
}
}
return new_array;
}
var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]
You can't avoid the loop, but you can add more semantics to the code by making a function that explains what you're doing.
If, for some reason, you are not comfortable with splice or filter methods, there is a nice (outdated, but still working) method by John Resig: http://ejohn.org/blog/javascript-array-remove/

Need help with setting multiple array values to null in a loop - javascript

I have been working on creating a custom script to help manage a secret questions form for a login page. I am trying to make all the seperate select lists dynamic, in that if a user selects a question in one, it will no longer be an option in the rest, and so on. Anyways, the problem I am having is when I try to set the variables in the other lists to null. I am currently working with only 3 lists, so I look at one list, and find/delete matches in the other 2 lists. Here is my loop for deleting any matches.
for(i=0; i<array1.length; i++) {
if(array2[i].value == txtbox1.value) {
document.questions.questions2.options[i] = null
}
if(array3[i].value == txtbox1.value) {
document.questions.questions3.options[i] = null
}
}
This works fine if both the matches are located at the same value/position in the array. But if one match is at array1[1] and the other match is at array3[7] for example, then only the first match gets deleted and not the second. Is there something I am missing? Any help is appreciated. Thanks!
I don't see too many choices here, considering that the position in each array can vary.
Do it in separate loops, unless of course you repeat values in both arrays and share the same position
EDTI I figured out a simple solution, it may work, create a function. How about a function wich recives an array as parameter.
Something like this:
function finder(var array[], var valueToFound, var question) {
for (i=0; i<array.lenght; i++) {
if (array[i].value == valueToFound) {
switch (question) {
case 1: document.questions.questions1.options[i] = null;
break;
}
return;
}
}
}
I think i make my point, perhaps it can take you in the right direction
My bet is that the code isn't getting to array3[7] because either it doesn't exist or that array2 is too short and you're getting a JavaScript exception that's stopping the code from doing the check. Is it possible that array2 and array3 are shorter than array1?
It is more code, but I would do it like this:
var selectedvalue == txtbox1.value;
for(i=0; i<array2.length; i++) { // iterate over the length of array2, not array1
if(array2[i].value == selectedvalue) {
document.questions.questions2.options[i] = null;
break; // found it, move on
}
}
for(i=0; i<array3.length; i++) {
if(array3[i].value == selectedvalue) {
document.questions.questions3.options[i] = null;
break; // you're done
}
}

Categories