1

I am new to javascript and am trying to create a simple price calculator in javascript for my website (cupcake shop website). first, i have the user choose from a list of flavor choices and post that to a textarea, when the user selects vegan flavors, i would add $3.50 to the end when posting to the textarea, and when the user selects non vegan, the price is $2.75. everything posted to the textarea are strings. so for the calculator, I extract the last 5 characters (which is the price) and add them up to get a subtotal. I used a loop to go thru the list in the textarea to extract the numbers one by one and add them up, however, the calculation came out wrong. Can anyone tell me what is wrong with it???

function addOption(selectbox,text,value )
{
var optn = document.createElement("OPTION");
optn.text = text;
optn.value = value;
selectbox.options.add(optn);
}

function addOption_list(selectbox){
var flavors = new Array("blueberry vegan","butterscotch","cappuccino vegan","carrot     
cake","carrot vegan","chocolate walnut vegan",
"chocolate peanut butter vegan","green tea & chocolate","keylime","lavender     
vegan","lemon","lemon cream vegan","mandarin orange",
"mint chocolate","mocha","peanut butter & double chocolate","raspberry swirl","red   
velvet","sesame","sesame oreo",
"strawberry","strawberry 2 vegan","tangerine","thia tea","triple   
chocolate","vanilla","very berry","vietnamese coffee","yuzu");
for (var i=0; i < flavors.length;++i){

addOption(document.drop_list.flavors_list, flavors[i], flavors[i]);
}
}

var $ = function (id){
return document.getElementById(id);
}

var cart = [];
var update_cart = function(){
if(cart.length == 0){
    $("flavor_name").value = "";
} else{
    var list = "";
    for(var i in cart){ 
        list += (parseInt(i) +1) + ". " + cart[i] + /*":  $3.50" + */ "\n"; 
    }
    $("flavor_name").value = list;


    var sum = 0;
    for(var i = 0; i < cart.length; i++){
        var price = parseFloat(list.substring(list.length - 5));
        sum += parseFloat( price.toFixed(2) );
    }

    $("subtotal").value = sum;
    var tax = parseFloat(($("subtotal").value * .08875).toFixed(2));
    $("sales_tax").value = tax;

    var total = parseFloat(price + tax).toFixed(2);
    $("total").value = total;

    }
}




var selectDropdown = function(){
    var dropdownValue=document.getElementById("flavors_list").value;
   // alert("You selected : " + dropdownValue);


if(dropdownValue == "blueberry vegan" || dropdownValue == "cappuccino vegan" ||   
       dropdownValue == "carrot vegan" || dropdownValue == "carrot vegan" ||
   dropdownValue == "chocolate walnut vegan" || dropdownValue == "chocolate peanut   
       butter vegan" || dropdownValue == "lavender vegan" || dropdownValue == "lemon   
       cream vegan" || dropdownValue == "strawberry 2 vegan"){
    alert("Adding " + dropdownValue + " to cart.");
    cart[cart.length] = dropdownValue + ":  $3.25";
    update_cart();
} else{
    alert("Adding " + dropdownValue + " to cart.");
    cart[cart.length] = dropdownValue + ":  $2.75";
    update_cart();
} 
}

HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"     
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Home-Little Sweetie Cupcakes</title>
<link href="little_sweetie_styles.css" rel="stylesheet" type="text/css" />
<link rel="Shortcut Icon" href="favicon.ico" />
<script type="text/javascript" src="invoice.js"></script>


</head>

<body onLoad="addOption_list()";>
<div class="cart_content">

    <span class="content_title">
        Price Calculator
    </span>
    <p class="bodytext">To place an order, please enter the desired flavor from  
               the dropdown list:</p>
    <FORM name="drop_list" action="yourpage.php" method="POST" >
        <SELECT NAME="flavors_list" id="flavors_list"  
                    onchange="selectDropdown()">
        <Option value="" >Flavors List</option>
        </SELECT>
    </form>
    <div>
    <!--<textarea id="shopping_cart" rows="5" cols="50"></textarea>-->
    <table id="invoice">
    <tr>
        <td width="150"><textarea id="flavor_name" rows="10" cols="40" 
                     disabled="disabled" /></textarea></td>
        <td>&nbsp;</td>
        <td id="calculator">
            <label class="bodytext">Subtotal: </label><input 
                                type="text" id="subtotal" size="10" disabled="disabled" 
                                  /><br />
            <label class="bodytext">Sales Tax: </label><input 
                                 type="text" id="sales_tax" size="10" 
                                  disabled="disabled" /><br />
            <label class="bodytext">Total: </label><input type="text" 
                                id="total" size="10" disabled="disabled" /><br />
        </td>

    </tr>

    </table>
           </body>
           </html>

Thanks in advance

k.lin
  • 11
  • 2

2 Answers2

2

I see multiple issues in your code, but without a working example to look at where we can see the actual HTML and values, it's hard to know for sure what all the issues are. Here are some of the issues:

  1. parseInt() should always be passed the second parameter (the radix) otherwise it will guess what the radix is based on the content.
  2. You should NEVER use the for (x in a) to iterate the contents of an array. That can include properties that are added to the array in addition to the array elements. It is much safer to use for (var i = 0; i < array.length; i++).
  3. Floating point math is not perfect so if you want perfect financial math, you should convert everything to a number of cents and do integer math on that.
  4. Neither parseInt() nor parseFloat() will tolerate a $ in the string so you must remove those first.
  5. When iterating an array with a for loop, the incrementing index is just the index into the array. You have to actually reach into the array and get each value.

In this block of code, it's unclear what you're trying to iterate. Every pass through the for loop is just using the same value of list. It's not iterating an array.

for(var i = 0; i < cart.length; i++){
    var price = parseFloat(list.substring(list.length - 5));
    sum += parseFloat( price.toFixed(2) );
}

FYI, it's much safe to extract a number from the end of your string like this:

function getNumber(str) {
    var matches = str.match(/([\d\.]+)\s*$/)
    if (matches) {
        return (+matches[1]);
    }
    return(0);
}

var str = "xxx $99.45\n";
var num = getNumber(str);
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I don't think he gets errors with his `parseFloat`: The string he uses is something like `"… $3.50\n".substr(-5)` :-| – Bergi Aug 05 '12 at 00:53
  • @Bergi - it's not completely obvious because we don't know the possible range and certainly the code isn't safe against variable length numbers (like something greater than $9.99). It would be much safer to extract all trailing digits and periods or something like that rather than extracting a fixed length string. – jfriend00 Aug 05 '12 at 01:07
  • 1
    I added to my answer a safer way to extract the number from your string. – jfriend00 Aug 05 '12 at 01:25
  • ...but I'm quite sure that he actually does not *want* to extract them from the end of the string :-| – Bergi Aug 05 '12 at 01:31
  • @Bergi - it's unclear what the input actually looks like (I hate javascript questions that operate on phantom HTML that we haven't been given), but this part of the OP's code looks like it's extracting from the end: `list.substring(list.length - 5)`. – jfriend00 Aug 05 '12 at 01:46
  • thanks for the comments everyone! i edited my post to include the entire JS code and HTML. after i insert the string to textarea, it would come out like this: 1. blueberry: $3.50 and for the calculation, i am trying to extract $3.50 and convert it to a number – k.lin Aug 05 '12 at 02:03
  • UPDATE: after using jfriend00's method to extract numbers, the calculations came out correct! thanks jfriend00! and thanks for everyone's helpful comments! – k.lin Aug 05 '12 at 02:55
1
for(var i in cart)

You should traverse the array with a for-loop and a counting variable, as you do below. Additional benefit: No need to parseInt the property names.

I extract the last 5 characters (which is the price) and add them up to get a subtotal:

for(var i = 0; i < cart.length; i++){
    var price = parseFloat(list.substring(list.length - 5));
    sum += parseFloat( price.toFixed(2) );
}

Yes, for every item in cart you get the price out of the last five characters of the (same) list string. WTF?

What you wanted was to get the price for each item in the cart. And I'm quite sure there are not only <$10 items which have only 4 characters in their string representation. So, remove the first character (the dollar sign) of a cart item and apply parseFloat on that:

for (var i=0; i<cart.length; i++) {
    var price = parseFloat(cart[i].substring(1));
    sum += price;
}

Also, you should not use parseFloat( price.toFixed(2) );. Apart from some IE bugs, floating point maths will never be reliable, and does not get better when constantly rounding. Instead compute with the integer number of cents (although JS technically has no integers):

var price = Math.floor(parseFloat(str, 10) * 100);
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375