The Web Design Group

... Making the Web accessible to all.

Welcome Guest ( Log In | Register )

 
Reply to this topicStart new topic
> Loop through all fields, change to 0 if NaN
Terminator
post Jun 19 2016, 02:33 PM
Post #1


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



I want all textfields to contain either a number input by the user, or 0 since these are being added together for a budget calculator.

http://examples.mrwdesign.net/js/budget/budget.html

Right now only the "Home" part of the form is coded. I you remove the zero from Mortgage and leave it blank, it automatically changes back to 0 instead of displaying NaN.

CODE

    if (isNaN(mortgage)) {
        mortgage = 0;
    }


I would like to do this for all input fields with a loop. They have a class name as "budget" and was wondering if using getElementsByClassName might be the best way. I used getElementsByClassName to set all of these textfields to 0 when you hit the reset button:

CODE

    var budgetText = document.getElementsByClassName("budget");
    for (var i = 0; i < budgetText.length; i++) {
        budgetText[i].value = "0";
    }


Can I do something similar to set the values of any fields to 0 if the user deletes them? I use Regex to allow only positive and negative numbers with a decimal point, so all I want is either they leave the textfield as 0, or change it to a number. Regex covers everything except for if they delete the zero and submit with the textfield blank.

CODE

var mortgage = parseFloat($("mortgage").value.replace(/[^\d\.\-]/g, ''));


I would need to check if all fields in the budget class are NaN, but was having issues with that part.

This post has been edited by Terminator: Jun 19 2016, 02:36 PM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 19 2016, 03:02 PM
Post #2


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ Jun 19 2016, 09:33 PM) *

I you remove the zero from Mortgage and leave it blank, it automatically changes back to 0 instead of displaying NaN.

CODE

    if (isNaN(mortgage)) {
        mortgage = 0;
    }


I would like to do this for all input fields with a loop. They have a class name as "budget" and was wondering if using getElementsByClassName might be the best way.

Sounds good to me.

QUOTE
I used getElementsByClassName to set all of these textfields to 0 when you hit the reset button:

CODE

    var budgetText = document.getElementsByClassName("budget");
    for (var i = 0; i < budgetText.length; i++) {
        budgetText[i].value = "0";
    }


Can I do something similar to set the values of any fields to 0 if the user deletes them?

Do you want to change the INPUT elements' values (as seen by a user), or the corresponding javascript variable values (as used by the calculation script)? As a user, I might consider the number 0 and an empty field equivalent, so I'm not sure if it would really benefit the user to force-change an empty INPUT field to 0. Make the change in the script instead.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post Jun 19 2016, 03:09 PM
Post #3


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ Jun 19 2016, 03:02 PM) *

Do you want to change the INPUT elements' values (as seen by a user), or the corresponding javascript variable values (as used by the calculation script)? As a user, I might consider the number 0 and an empty field equivalent, so I'm not sure if it would really benefit the user to force-change an empty INPUT field to 0. Make the change in the script instead.


Input and Calculation. It wont calculate correctly if using as NaN. But the reason why I wanted to change the user display as well is because if they remove the 0 it will say NaN in the text field.

Well if it can be left blank then that might be better than all 0's. I wasn't sure how to make it so that if they leave them blank it wont show NaN. They weren't adding together correctly when one of them was left blank, which is why I was setting them all to 0 by default.

I just couldn't figure out how to make the for loop do this, I know I probably need to check if each one in the budget class is NaN, then I wanted to change value to 0 if it is blank/NaN.

This post has been edited by Terminator: Jun 19 2016, 03:13 PM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 19 2016, 03:28 PM
Post #4


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ Jun 19 2016, 10:09 PM) *

the reason why I wanted to change the user display as well is because if they remove the 0 it will say NaN in the text field.

If you mean here:

CODE
    // display results
    $("mortgage").value = mortgage;

That can be avoided by replacing empty values with 0 in the very calculation script.

QUOTE
I just couldn't figure out how to make the for loop do this, I know I probably need to check if each one in the budget class is NaN, then I wanted to change value to 0 if it is blank/NaN.

Perhaps you could loop through the INPUT fields, then store each sanitized value as an array item instead of creating separate variables for each INPUT:

CODE
var budget=document.getElementsByClassName('budget');
var budgetText=new Array();
for(var i=0; i<budget.length; i++)
{
    budgetText[i]=parseFloat(budget[i].value.replace(/[^\d\.\-]/g, ''));
}

(I haven't tested the above, also it requires that each form field uses the same regex).

This post has been edited by Christian J: Jun 19 2016, 03:36 PM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post Jun 19 2016, 09:41 PM
Post #5


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ Jun 19 2016, 03:28 PM) *

Perhaps you could loop through the INPUT fields, then store each sanitized value as an array item instead of creating separate variables for each INPUT:

CODE
var budget=document.getElementsByClassName('budget');
var budgetText=new Array();
for(var i=0; i<budget.length; i++)
{
    budgetText[i]=parseFloat(budget[i].value.replace(/[^\d\.\-]/g, ''));
}

(I haven't tested the above, also it requires that each form field uses the same regex).


Thanks, there are a lot of textfields so a loop would help.

2 questions, there is no $ (getelementbyid) that is supposed to be part of the above code, maybe like this?

CODE

budgetText[i] = parseFloat($(budget[i].value.replace(/[^\d\.\-]/g, '')));


And secondly, since I am not declaring each variable individually, how would I do the calculations?

This is how the Home part is added together:

CODE

    var homeTotal = mortgage + propertyTax;  + homeInsurance + homeMaintenance +
                    lawnCare + hoa + homeOther;


but everything after homeTotal is no longer declared as a var, do I need to declare all these individually?

Thanks for your help.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 20 2016, 06:15 AM
Post #6


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ Jun 20 2016, 04:41 AM) *

2 questions, there is no $ (getelementbyid) that is supposed to be part of the above code, maybe like this?

CODE

budgetText[i] = parseFloat($(budget[i].value.replace(/[^\d\.\-]/g, '')));


No, getelementbyid is not needed when you use getElementsByClassName.

QUOTE
And secondly, since I am not declaring each variable individually, how would I do the calculations?

This is how the Home part is added together:

CODE

    var homeTotal = mortgage + propertyTax;  + homeInsurance + homeMaintenance +
                    lawnCare + hoa + homeOther;


but everything after homeTotal is no longer declared as a var, do I need to declare all these individually?

No, just add the array item values together:

CODE
var homeTotal = budgetText[0] + budgetText[1] ...etc;


QUOTE
Thanks for your help.

You're welcome!
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post Jun 20 2016, 10:49 AM
Post #7


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



Thanks Christian, that worked great. Do you know how I should change the empty values to zero? I have tried a few variations using If (isNaN) but could not get it to work. Here is what the calculation script looks like:

CODE

var homeTotal = budgetText[0] + budgetText[1] + budgetText[2] + budgetText[3] +
                    budgetText[4] + budgetText[5] + budgetText[6];


Then there will be more of these for the other sections like Automotive:

CODE

var autoTotal = budgetText[7] + budgetText[8] + budgetText[9] + budgetText[10] +
                    budgetText[11] + budgetText[12] + budgetText[13];


And another question, since I am using Regex to clean up the inputs is it possible to use some form of the getElementsByClassName to do a re-display on all textfields? Like if they typed in 1000abc for mortgage it would display as 1000 since Regex removes letters. Is it possible to do some kind of loop to display the inputs again, instead of having to type them all out individually like below?

CODE

$("mortgage").value = mortgage;
$("propertyTax").value = propertyTax;
etc, etc.......
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 20 2016, 11:39 AM
Post #8


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



This might work (using a single loop for everything), but I just tested briefly:

CODE

var budget=document.getElementsByClassName('budget');
var budgetText=new Array();
for(var i=0; i<budget.length; i++)
{
    budgetText[i]=parseFloat(budget[i].value.replace(/[^\d\.\-]/g, ''));
    if(isNaN(budgetText[i]))
    {
        budgetText[i]=0; // Use no quotes around the 0, since that would convert it to a string.
    }
    budget[i].value=budgetText[i];
}
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post Jun 20 2016, 01:55 PM
Post #9


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ Jun 20 2016, 11:39 AM) *

This might work (using a single loop for everything), but I just tested briefly:


Thank you so much, it works great and much less code than I originally thought. I am going to use this technique on future programs when needed.

http://examples.mrwdesign.net/js/budget/budget.html

Thanks for suggesting to do the loop this way, feel free to make any improvement suggestions anytime.

In school they only teach us the long way to do things, and an instructor told me that there is no way around not having to put parseFloat($("income").value.replace(/[^\d\.\-]/g, '')); for every single text field and cannot be looped, which of course you proved wrong.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 20 2016, 03:31 PM
Post #10


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ Jun 20 2016, 08:55 PM) *

Thank you so much, it works great and much less code than I originally thought. I am going to use this technique on future programs when needed.

You're welcome.

Another idea is to use an "associative array" Object* with IDs instead of getElementsByClassName. It's more verbose, but perhaps makes the script more descriptive than when using a numerical array:

CODE
<input type="text" id="mortgage" value="">
<input type="text" id="property_tax" value="">
<input type="text" id="insurance" value="">


var budgetText=new Object();

// each textfield's ID value is used as a property of budgetText:
budgetText.mortgage=0;
budgetText.property_tax=0;
budgetText.insurance=0;

for(i in budgetText)
{
    var budget_field=document.getElementById(i); // ID is taken from budgetText
    budgetText[i]=parseFloat(budget_field.value.replace(/[^\d\.\-]/g, ''));
    if(isNaN(budgetText[i]))
    {
        budgetText[i]=0;
    }
    budget_field.value=budgetText[i];

}

var homeTotal = budgetText.mortgage + budgetText.property_tax + budgetText.insurance;

* Technically it seems there are no associative arrays in javascript, but objects apparently work in the same way: http://andrewdupont.net/2006/05/18/javascr...idered-harmful/

QUOTE
http://examples.mrwdesign.net/js/budget/budget.html

Thanks for suggesting to do the loop this way, feel free to make any improvement suggestions anytime.

Looks good. Perhaps you could use a function with arguments to add the various "totals" fields together?

Of course as soon as one field needs a different regex than the rest, using a loop becomes a limitation...

QUOTE
In school they only teach us the long way to do things, and an instructor told me that there is no way around not having to put parseFloat($("income").value.replace(/[^\d\.\-]/g, '')); for every single text field and cannot be looped, which of course you proved wrong.

Maybe they want to save more complex solutions for later, or maybe they just want you to practice typing. happy.gif
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post Jun 20 2016, 11:19 PM
Post #11


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ Jun 20 2016, 03:31 PM) *


Another idea is to use an "associative array" Object* with IDs instead of getElementsByClassName. It's more verbose, but perhaps makes the script more descriptive than when using a numerical array:

CODE
<input type="text" id="mortgage" value="">
<input type="text" id="property_tax" value="">
<input type="text" id="insurance" value="">


var budgetText=new Object();

// each textfield's ID value is used as a property of budgetText:
budgetText.mortgage=0;
budgetText.property_tax=0;
budgetText.insurance=0;

for(i in budgetText)
{
    var budget_field=document.getElementById(i); // ID is taken from budgetText
    budgetText[i]=parseFloat(budget_field.value.replace(/[^\d\.\-]/g, ''));
    if(isNaN(budgetText[i]))
    {
        budgetText[i]=0;
    }
    budget_field.value=budgetText[i];

}

var homeTotal = budgetText.mortgage + budgetText.property_tax + budgetText.insurance;



I do like the idea because you see how the textfields are being added below, I have to input the numbers which doesn't indicate which field is which.

CODE

        var homeTotal = budgetText[0] + budgetText[1] + budgetText[2] + budgetText[3] +
                        budgetText[4] + budgetText[5] + budgetText[6];


But it looks like I would need to declare all 35 input textfields separately like below?

CODE

budgetText.mortgage=0;
budgetText.property_tax=0;
budgetText.insurance=0;
etc
etc
.........


QUOTE

Looks good. Perhaps you could use a function with arguments to add the various "totals" fields together?


I wanted to do that, I haven't done a lot with objects in JavaScript yet, but I have in C#, so that knowledge might help.

QUOTE

Of course as soon as one field needs a different regex than the rest, using a loop becomes a limitation...


In this case it is fine since they are all using the same Regex. But Total Take Home Pay is validated a different way so it is separate. If I had a group doing different Regex I could always give that group of textfields a different class and array name, and their own separate loop coded similar.

QUOTE

Maybe they want to save more complex solutions for later, or maybe they just want you to practice typing. happy.gif


I think they just teach the same class for so many years that it is routine for them to just do what the textbook says, and the textbooks are usually for beginners and wont always do advanced stuff like this loop you created.

This post has been edited by Terminator: Jun 20 2016, 11:20 PM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 21 2016, 08:33 AM
Post #12


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ Jun 21 2016, 06:19 AM) *

But it looks like I would need to declare all 35 input textfields separately like below?

True. Here's an idea using both CLASS and ID, where the object names are generated in the loop:

CODE
<input type="text" class="budget" id="mortgage" value="">
<input type="text" class="budget" id="property_tax" value="">
<input type="text" class="budget" id="insurance" value="">



var budgetText=new Object();
var budget=document.getElementsByClassName('budget');

for(var i=0; i<budget.length; i++)
{
    // The budgetText property name is taken from the textfield ID,
    // while the property value is from the textfield value.
    var curID=budget[i].id;
    budgetText[curID]=parseFloat(budget[i].value.replace(/[^\d\.\-]/g, ''));

    if(isNaN(budgetText[curID]))
    {
        budgetText[curID]=0;
    }
    budget[i].value=budgetText[curID];
}

homeTotal = budgetText.mortgage + budgetText.property_tax + budgetText.insurance;

Thanks for asking about this, now I learned something new. smile.gif
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 21 2016, 08:48 AM
Post #13


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



Here's a similar script using HTML5 data- attributes instead of CLASS and ID. The data-budget attributes can be looped through with querySelectorAll, similar to getElementsByClassName:

CODE
<input type="text" data-budget="mortgage" value="">
<input type="text" data-budget="property_tax" value="">
<input type="text" data-budget="insurance" value="">



var budgetText=new Object();
var dataBudget=document.querySelectorAll('[data-budget]');

for(var i=0; i<dataBudget.length; i++)
{
    // The budgetText property name is taken from the HTML5
    // custom "data-budget" attribute. The property value is from the textfield value.
    var budgetPost=dataBudget[i].getAttribute('data-budget');
    budgetText[budgetPost]=parseFloat(dataBudget[i].value.replace(/[^\d\.\-]/g, ''));

    if(isNaN(budgetText[budgetPost]))
    {
        budgetText[budgetPost]=0;
    }
    dataBudget[i].value=budgetText[budgetPost];
}

homeTotal = budgetText.mortgage + budgetText.property_tax + budgetText.insurance;

(Sorry for my confusing variable names blush.gif )
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post Jun 21 2016, 11:49 AM
Post #14


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



Thanks, I went with the first choice and it seems to be working great. This is much better using budgetText.mortgage vs budgetText[0] to add the totals.

http://examples.mrwdesign.net/js/budget/budget.html

Here is the entire JavaScript code. Do you think I should move the validateIncome function and the calculate totals to other js files?

CODE

function calculateBudget() {
    // income input
    var income = parseFloat($("income").value.replace(/[^\d\.\-]/g, ''));
    var isValid = true;
    validateIncome(income);
    
    // validate income
    function validateIncome(income) {
        if (isNaN(income) || income < 0 || income > 1000000) {
            $("income_error").innerHTML = "Must be between 0 and 1 Million" + "<br><br>";
            $("income").focus();
            isValid = false;
        } else {
            $("income_error").innerHTML = "";
            isValid = true;
        }
    };
    
    if (isValid) {
        // get user input, set to zero if blank
        var budgetText = new Object();
        var budget = document.getElementsByClassName("budget");

        for (var i = 0; i < budget.length; i++) {
            var curID = budget[i].id;
            budgetText[curID] = parseFloat(budget[i].value.replace(/[^\d\.\-]/g, ''));

            if (isNaN(budgetText[curID])){
                budgetText[curID] = 0;
            }
            budget[i].value = budgetText[curID];
        }
        
        // calculate totals
        var homeTotal = budgetText.mortgage + budgetText.propertyTax + budgetText.homeInsurance +
                        budgetText.homeMaintenance + budgetText.lawnCare + budgetText.hoa +
                        budgetText.homeOther;
                        
        var autoTotal = budgetText.autoLoan + budgetText.autoInsurance + budgetText.tolls +
                        budgetText.detail + budgetText.autoMaintenance +
                        budgetText.autoFuel + budgetText.autoOther;
                        
        var utilityTotal = budgetText.gas + budgetText.electricity + budgetText.water +
                            budgetText.alarm + budgetText.internet + budgetText.phone +
                            budgetText.utilityOther;

        var livingTotal = budgetText.groceries + budgetText.personalCare + budgetText.medical +
                            budgetText.clothing + budgetText.childCare +
                            budgetText.entertainment + budgetText.livingOther;    

        var financeTotal = budgetText.creditCards + budgetText.savings + budgetText.investments +
                            budgetText.retirement + budgetText.studentLoan +
                            budgetText.lifeInsurance + budgetText.financeOther;

        var budgetTotal = homeTotal + autoTotal + utilityTotal + livingTotal + financeTotal;
            
        var difference = income - budgetTotal;
        
        // display calculated results
        $("homeTotal").value = homeTotal.toFixed(2);
        $("autoTotal").value = autoTotal.toFixed(2);    
        $("utilityTotal").value = utilityTotal.toFixed(2);
        $("livingTotal").value = livingTotal.toFixed(2);
        $("financeTotal").value = financeTotal.toFixed(2);
        $("income").value = income.toFixed(2);
        $("budgetTotal").value = budgetTotal.toFixed(2);
        $("difference").value = difference.toFixed(2);
    }
};

function resetForm() {
    $("income_error").innerHTML = "";
    $("mortgage").focus();
};

window.onload = function() {
    $("calculate").onclick = calculateBudget;
    $("reset").onclick = resetForm;
    $("mortgage").focus();
};


This post has been edited by Terminator: Jun 21 2016, 11:51 AM
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 21 2016, 03:15 PM
Post #15


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ Jun 21 2016, 06:49 PM) *

Do you think I should move the validateIncome function and the calculate totals to other js files?

Only if you think a single file is too large to be practical, or maybe if you're going to use the same files for other projects. Note that multiple files may slow down page load, because the browser must make more HTTP requests, which easily becomes a bottleneck on many of today's sites (together with all stylesheets, images etc).

The largest javascript I've worked on grew to almost 3000 lines in total, but long before that I had to split it up because scrolling up and down in the text editor became too tedious. I then used PHP to stitch the pieces back together into a single file, so I could save the number of HTTP requests in the browser.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Terminator
post Jun 21 2016, 11:02 PM
Post #16


Advanced Member
****

Group: Members
Posts: 218
Joined: 19-March 15
Member No.: 22,398



QUOTE(Christian J @ Jun 21 2016, 03:15 PM) *

Only if you think a single file is too large to be practical, or maybe if you're going to use the same files for other projects. Note that multiple files may slow down page load, because the browser must make more HTTP requests, which easily becomes a bottleneck on many of today's sites (together with all stylesheets, images etc).

The largest javascript I've worked on grew to almost 3000 lines in total, but long before that I had to split it up because scrolling up and down in the text editor became too tedious. I then used PHP to stitch the pieces back together into a single file, so I could save the number of HTTP requests in the browser.


In this case there isn't much code so I will leave it all on one file. Thanks again for suggesting this loop, I will be using this often in the future. Do you happen to know C# by any chance?
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post
Christian J
post Jun 22 2016, 05:02 AM
Post #17


.
********

Group: WDG Moderators
Posts: 9,648
Joined: 10-August 06
Member No.: 7



QUOTE(Terminator @ Jun 22 2016, 06:02 AM) *

Do you happen to know C# by any chance?

No, nothing. Just a little JS and PHP.
User is offlinePM
Go to the top of the page
Toggle Multi-post QuotingQuote Post

Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 



- Lo-Fi Version Time is now: 16th April 2024 - 01:12 AM