jQuery Calculation Plug-in: Making calculating easy...

Categories: JavaScript, jQuery

I actually wrote this plug-in months ago, but only made passing mention about in on the jQuery mailing list. Since the topic of dynamic calculation came up again today on the mailing list, I thought I'd go ahead and officially announce the plug-in here.

My jQuery Calculation Plug-in was designed as a generic mathematical library to make it easy to do things like sum or average values that are displayed on the page. For example, to get the sum of all of the elements with the class of "price" you'd use:

alert( $(".price").sum() );

Since I did not want to restrict calculation to only form elements, the plug-in has helper method called parseNumber() which is called internally which parses a numeric value from an element. It can parse both form elements and normal HTML elements (like <div> or <span> elements.) In order to allow parsing of HTML elements that might contain additional formatting in the element, I'm using a regular expression of /\d+(,\d{3})*(\.\d{1,})?/g to parse the contents of the element to find the first thing that looks like a number. And don't worry, you can configure the regular expression if it doesn't meet your needs.

This plug-in actually gives you a lot of power. For example, let's say we have a form that looks like:

calc.plugin.cart

Wouldn't it be great if you could update all the calculations on the screen without writing a ton of code? Well, this is where the Calculation Plug-in really shines. The HTML for the table above is:

<table width="500">
<col style="width: 50px;" />
<col />
<col style="width: 60px;" />
<col style="width: 110px;" />
<tr>
   <th>
      Qty
   </th>
   <th align="left">
      Product
   </th>
   <th>
      Price
   </th>
   <th>
      Total
   </th>
</tr>
<tr>
   <td align="center">
      <input type="text" name="qty_item_1" id="qty_item_1" value="1" size="2" />
   </td>
   <td>
      <a href="http://www.packtpub.com/jQuery/book">Learning jQuery</a>
   </td>
   <td align="center" id="price_item_1">
      $39.99
   </td>
   <td align="center" id="total_item_1">
      $39.99
   </td>
</tr>
<tr>
   <td align="center">
      <input type="text" name="qty_item_2" id="qty_item_2" value="1" size="2" />
   </td>
   <td>
      <a href="http://jquery.com/">jQuery Donation</a>
   </td>
   <td align="center" id="price_item_2">
      $14.99
   </td>
   <td align="center" id="total_item_2">
      $14.99
   </td>
</tr>
<tr>
   <td colspan="3" align="right">
      <strong>Grand Total:</strong>
   </td>
   <td align="center" id="grandTotal">
   </td>
</tr>
</table>

In order to hook our form up so that all of the totals are automatically calculated on the fly, we just need to bind a function to the quantity fields to be triggered on each key press:

// bind the recalc function to the quantity fields
$("input[@name^=qty_item_]").bind("keyup", recalc);

Our recalc() function looks like this:

function recalc(){
   // run the calc() method on each of the "total" fields
   $("[@id^=total_item]").calc(
      // the equation to use for the calculation
      "qty * price",
      // we now define the values for the variables defined in the equation above
      {
         // instead of using a static value, we use a jQuery object which grabs all the quantities
         qty: $("input[@name^=qty_item_]"),
         // now we define the jQuery object which reads in the "price" from the table cell
         price: $("[@id^=price_item_]")
      },
      // this function is execute after the calculation is completed, which allows us to
      // add formatting to our value
      function (s){
         // return the number as a dollar amount
         return "$" + s.toFixed(2);
      },
      // once all calculations are completed, we execute the code below
      function ($this){
         // now we get the sum() of all the values we just calculated
         var sum = $this.sum();

         // now that we have the grand total, we must update the screen
         $("#grandTotal").text(
            // round the results to 2 digits
            "$" + sum.toFixed(2)
         );
      }
   );
}

Well this code may look a little overwhelming at first, it's actually very easy to implement. You can see this example code in action on the Calculation Plug-in's home page.

The one big "gotcha" with using jQuery selectors as variables for the equations is that your selectors must return arrays of equal size. The calc() engine needs matching arrays, so that it can correct apply the calculations to each position in the array. For example, the above code actually translates to:

qty[0] * price[0] = total[0]
qty[1] * price[1] = total[1]

I think for the most part this should be a big deal, but I guess if you had a really strange layout trying to get your selectors to match up array items correctly could be programmatic. Unfortunately, I'm not sure there's a solution to that problem that doesn't involve a lot of complexity.

Run example code

Related Blog Entries

Comments

Rui Gomes's Gravatar hi,
this plug-in doesn´t work on IE 7.


regards,
Rui
Dan G. Switzer, II's Gravatar @Rui:

I just checked this in IE7 and it's working fine for me. Is there something specific that's not working for you?
Rui Gomes's Gravatar hi, thks for the reply.

if tested with your demo link and nothing (calculations) it´s working, but it works fine on FF.

regards,
Dan G. Switzer, II's Gravatar @Rui:

I tested the code on my example page under IE7 and everything worked as expected. Perhaps you have something filtering/blocking content that's running under IE7.

What errors are you seeing? I have tested the code under two different IE7 installations and the page works on both machines. It also works under IE6 for me.
Rui Gomes's Gravatar ok.

...and what might be filtering/blocking ?
i don´t have any firewall installed and i´ve stopped my anti-virus also.
Rui Gomes's Gravatar if you want i´ll send you a print screen of it.
Rui Gomes's Gravatar i´ll test on another IE7, ok ?

regards,
Dan G. Switzer, II's Gravatar @Rui:

One culprit might be my check to see if Firefox is installed:

bIsFirebugReady = (!!window.console && !!window.console.log);

In normal circumstances, IE7 should not return true for that line--but perhaps you have something installed (like a debugger) which is causing the bIsFirebugReady check to be true.

I'll certainly look into any errors you post for me.
Rui Gomes's Gravatar ok, thks again for the reply.

regards,
Dan G. Switzer, II's Gravatar @Rui:

Whatever info you can give me, I'll definitely try to fix the problem. It's just hard to address when you can't replicate and without more information on the error(s) you're getting, there's not a whole lot I can do. :)
Fontzter's Gravatar Hi Dan,

Thanks for the great plug-in. One question/feature request: It does not handle negative numbers. I think I was able to overcome this by changing the regex to allow an minus sign. Do you think this will suffice?

reNumbers: /-?\d+(,\d{3})*(\.\d{1,})?/g

I suspect that is why you have the regex as an option. My point in writing was merely to ask if my regex would work okay and allow negative numbers; and, if so, to suggest that you may want to make this the default. For example, on your demo page, if you enter a negative number, it actually sums or averages the absolute of it, which is a little misleading.

Thanks again for the great plug-in!

Dave
Dan G. Switzer, II's Gravatar @Dave:

That's definitely one of the reasons I saw the need to make the regex configurable. Plus, European number formatting is different from the US.

Adding support for negative numbers was definitely an oversight on my part. The regex you have should work fine.

As soon as I get a chance to do some more testing, I'll add the check for negative numbers to the next revision.

I think the work I did last week to the sum() and avg() methods make it much easier to use. I'm always open to other suggestions as well.
Dan G. Switzer, II's Gravatar @Dave:

I updated the code this weekend and added support for negative numbers. Please test the new build and let me know how it works. I also added some callback methods for handling when the parseNumber() method can't find a numeric value in the element.
Venkat's Gravatar Hi Dan,

Thanks for the nice plug-in.
How to calculate the time can u pl give an idea

Thanks
Venkat
:)
balboa's Gravatar how do i do calculation like this: (qty * price) + fee. is there another event other than keyup? coz i use select box to trigger the calculation
Dan G. Switzer, II's Gravatar @balboa:

You can use whatever event you want to trigger the re-running of the $.calc() method--my example just uses the keyup. You could use my example pretty much verbatim, but just change the "keyup" event to the event you want to use (for a select box, you'll whant "change".)

Add Comment



If you subscribe, any new posts to this thread will be sent to your email address.