user1305063
user1305063

Reputation: 67

Jquery calculation not working properly

I have a little piece of jQuery code which calculates the recipe ingredients as per given serving, but somewhere it's not working properly.

My code is like this:

Serving: <input type="text" name="serving" class="serving" value="5" /> persons
<input type="hidden" id="previousServing" value="5"/>

        <h3>ingredients</h3>
        <ul class="ingredients">
        <li class="ingredient">
        <span class="amount">1</span> cups
        <span class="name"><a href="http://www.mysite.com/ingredient/yogurt/">yogurt</a></span>
        </li>

        <li class="ingredient">
        <span class="amount">2</span> tbsp
        <span class="name"><a href="http://www.mysite.com/ingredient/yogurt/">chillies</a></span>
        </li>

        <li class="ingredient">
        <span class="amount">3</span> pieces
        <span class="name"><a href="http://www.mysite.com/ingredient/yogurt/">butter</a></span>
        </li>
        </ul>



$(function() {
  $('.serving').bind('keyup', function(event) {
    var previousValue = parseFloat($("#previousServing").val());
    var newValue = parseFloat($(event.target).val());
    if (previousValue && newValue) {
        $('.ingredient').each(function(index, elem) {
            var ingredientNow = $('.amount', elem);
            var oldIngredientAmount = ingredientNow.text();
            var newIngredientAmount = oldIngredientAmount * newValue / previousValue;
            ingredientNow.text(newIngredientAmount);
        });
        $('#previousServing').val(newValue);
    }
});
});

http://jsfiddle.net/vansimke/tLWpr/

issues:

  1. change the serving to 1.5 and it will give unlimited decimal places
  2. set the serving back to original (5), decimals wont disappear

Required: either no decimals or round to absolute two digit, i.e., 2.08 should be 2.00.

Thanks in anticipation.

Upvotes: 1

Views: 482

Answers (2)

Matt Urtnowski
Matt Urtnowski

Reputation: 2566

First Problem: Significant figures and rounding

You need to times newIngredientAmount to get a number of sigfigs then use Math.round to round it to the nearest integer. Then divide the result by the number you multiplied by earlier

http://jsfiddle.net/vQbQ6/12/

Added this line

newIngredientAmount = Math.round(newIngredientAmount * 100) / 100;

To create

$(function() {
  $('.serving').bind('keyup', function(event) {
    var previousValue = parseFloat($("#previousServing").val());
    var newValue = parseFloat($(event.target).val());
    if (previousValue && newValue) {
        $('.ingredient').each(function(index, elem) {
            var ingredientNow = $('.amount', elem);
            var oldIngredientAmount = ingredientNow.text();
            var newIngredientAmount = oldIngredientAmount * newValue / previousValue;
            newIngredientAmount = Math.round(newIngredientAmount * 100) / 100; 
            ingredientNow.text(newIngredientAmount);
        });
        $('#previousServing').val(newValue);
    }
});

Second Problem: Jquery .data(key, value) solution

The problem you have with the decimal hanging around is a problem due to rounding and oldIngredientAmount * newValue / previousValue because some of these values could have been rounded. It seems to me that this is a bad way going about calulating the ingredient amount. You should instead by basing the math off of the inital ingredient ratios instead of the rounded derived numbers you calculate. You could use jquery .data() to record the inital values in the amount spans and do math off those numbers each time.

http://api.jquery.com/data/

Fiddler with .data() to preserve the initial ratio and use that in the math

http://jsfiddle.net/vQbQ6/14/

$(function() {
    $('.ingredient').each(function(index, elem){
        $this = $(this);
        $this.data('init', parseFloat($this.text()));
    });

    $('#previousServing').data('init', parseFloat($('#previousServing').val()));

    $('.serving').bind('keyup', function(event) {
        var previousValue = $("#previousServing").data('init');
        var newValue = parseFloat($(event.target).val());
        if (previousValue && newValue) {
            $('.ingredient').each(function(index, elem) {
                var ingredientNow = $('.amount', elem);
                var initIngredientAmount = $(this).data('init');
                var newIngredientAmount = initIngredientAmount * newValue / previousValue;
                newIngredientAmount = Math.round(newIngredientAmount * 100) / 100; 
                ingredientNow.text(newIngredientAmount);
            });
            $('#previousServing').val(newValue);
        }
    });
});

Upvotes: 2

Kay
Kay

Reputation: 70

Math.round(newIngredientAmount) will round the number for you.

However, I just played around with your code. I don't think it's reliable for you to use the previously calculated value, especially if you're rounding it. That will mess up all your ingredient ratios.

This is what I would do

Serving: <input type="text" name="serving" class="serving" value="5" /> persons
<input type="hidden" id="defaultServing" value="5"/>

            <h3>ingredients</h3>
            <ul class="ingredients">
            <li class="ingredient">
            <span class="amount" defaultAmount="1">1</span> cups
            <span class="name"><a href="http://www.mysite.com/ingredient/yogurt/">yogurt</a></span> 
            </li>

            <li class="ingredient">
            <span class="amount" defaultAmount="2">2</span> tbsp
            <span class="name"><a href="http://www.mysite.com/ingredient/yogurt/">chillies</a></span> 
            </li>

            <li class="ingredient">
            <span class="amount" defaultAmount="3">3</span> pieces
            <span class="name"><a href="http://www.mysite.com/ingredient/yogurt/">butter</a></span> 
            </li>
            </ul>

$(function() {
    $('.serving').bind('keyup', function(event) {
        var previousValue = parseFloat($("#defaultServing").val());
        var newValue = parseFloat($(event.target).val());
        if (previousValue && newValue) {
            $('.ingredient').each(function(index, elem) {
                var ingredientNow = $('.amount', elem);
                var oldIngredientAmount = ingredientNow.attr("defaultAmount");
                var newIngredientAmount = oldIngredientAmount * newValue / previousValue;
                // no decimal places
                // ingredientNow.text(Math.round(newIngredientAmount));
                // two decimal places
                ingredientNow.text(Math.round(newIngredientAmount*100)/100);
            });

        }
    });
});​

Upvotes: 0

Related Questions