Reputation: 545
I'm building a WordPress plugin that handles, among other things, invoices. The idea is that when I create a new invoice, the plugin will automatically generate one line with default values for each product currently available (stored elsewhere, and not always the same). The problem is that I want to be able to just enter quantities and let the plugin do all the math for me totaling up each line, then a subtotal, then a total for the whole invoice. But my invoice lines are generated dynamically, so I don't necessarily know how many I'm going to have. Here's what I mean:
The products are given to me by WordPress, and then I output a line on the invoice for each one that exists, while appending a number to the end of the input box names so that I'll be able to tell which is which when I save the information later.
//Define the contents of the invoice lines box
function wpdsd_invoice_lines_mb_contents(){
//Get an array off all available products, sorted by item position as set by user
$arguments = array( 'post_type' => 'wpdsd_product',
'numberposts' => '-1',
'order' => 'ASC',
'orderby' => 'meta_value',
'meta_key' => 'wpdsd_item_position',
);
$products = get_posts($arguments);
//Output an invoice line for each available product
$linenumber = 0;
foreach($products as $product){
?>
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_<?php echo $linenumber; ?>" value="<?php echo $product->ID; ?>">
<input type="text" class="wpdsd_item" name="wpdsd_item_<?php echo $linenumber; ?>" value="<?php echo $product->post_title; ?>">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_<?php echo $linenumber; ?>" placeholder="Enter Qty">
$<input type="number" class="wpdsd_price" name="wpdsd_price_<?php echo $linenumber; ?>" step="0.01" value="<?php echo $product->wpdsd_default_price; ?>">
$<span class="wpdsd_total" id="wpdsd_total_<?php echo $linenumber; ?>">0.00</span></br>
<?php
$linenumber++;
}
?><input type="hidden" name="wpdsd_number_of_lines" value="<?php echo $linenumber; ?>"><?php
}
Here's the HTML I get as a result:
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_0" value="71">
<input type="text" class="wpdsd_item" name="wpdsd_item_0" value="1m 8 Pin">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_0" placeholder="Enter Qty">
$<input type="number" class="wpdsd_price" name="wpdsd_price_0" step="0.01" value="4">
$<span class="wpdsd_total" id="wpdsd_total_0">0.00</span></br>
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_1" value="45">
<input type="text" class="wpdsd_item" name="wpdsd_item_1" value="3m USB Type-C">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_1" placeholder="Enter Qty">
$<input type="number" class="wpdsd_price" name="wpdsd_price_1" step="0.01" value="5.52">
$<span class="wpdsd_total" id="wpdsd_total_1">0.00</span></br>
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_2" value="76">
<input type="text" class="wpdsd_item" name="wpdsd_item_2" value="Wall Charger">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_2" placeholder="Enter Qty">
$<input type="number" class="wpdsd_price" name="wpdsd_price_2" step="0.01" value="4.69">
$<span class="wpdsd_total" id="wpdsd_total_2">0.00</span></br>
<input type="hidden" name="wpdsd_number_of_lines" value="3">
For each line on the invoice, I want the total to appear when I input the quantity. In other words,
wpdsd_total_0 = wpdsd_qty_0 * wpdsd_price_0
I also want to calculate the total of all lines, apply taxes, and calculate a grand total.
I've found other answers that show ways to tell JavaScript or jQuery to do the math, but you have to know the names of your input boxes and the element in which you're displaying the result. How might I tell JavaScript to do the math for each line, however many there happen to be?
Edit: I added some common classes, as suggested. But I'm still not sure what I would do next...
Upvotes: 2
Views: 130
Reputation: 3186
This can be done using the onInput
event. Simply add the callback method in the html like this: onInput="onInputChangeHandler(this)"
where this
is the current element the method is fired on. Read more about onInput
here.
Notice how it's only added for the quantity and value fields. Every time either of these fields change, the method will fire.
Also notice that I have grouped each row into it's own <div>
. This is so I can just call the parentNode
for the current item changed and have all the other elements ready to use. You would have to change the PHP to something like this, (also changed </br>
to <br />
).
foreach($products as $product){
?>
<div>
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_<?php echo $linenumber; ?>" value="<?php echo $product->ID; ?>">
<input type="text" class="wpdsd_item" name="wpdsd_item_<?php echo $linenumber; ?>" value="<?php echo $product->post_title; ?>">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_<?php echo $linenumber; ?>" placeholder="Enter Qty">
$<input type="number" class="wpdsd_price" name="wpdsd_price_<?php echo $linenumber; ?>" step="0.01" value="<?php echo $product->wpdsd_default_price; ?>">
$<span class="wpdsd_total" id="wpdsd_total_<?php echo $linenumber; ?>">0.00</span><br />
</div>
<?php
$linenumber++;
}
function onInputChangeHandler(obj) {
var parentObj = obj.parentNode; // Gets the <div>
var children = parentObj.children; // Gets all the inputs
var quantity = children[2].value; // Get the quantity
var value = children[3].value; // Get the value
children[4].innerHTML = Math.round((quantity * value)*100)/100; // Calculate the total for the total child: Math.round() * 100/100 will round to second decimal place
}
<div>
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_0" value="71">
<input type="text" class="wpdsd_item" name="wpdsd_item_0" value="1m 8 Pin">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_0" placeholder="Enter Qty" oninput="onInputChangeHandler(this)"> $
<input type="number" class="wpdsd_price" name="wpdsd_price_0" step="0.01" value="4" oninput="onInputChangeHandler(this)"> $
<span class="wpdsd_total" id="wpdsd_total_0">0.00</span><br />
</div>
<div>
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_1" value="45">
<input type="text" class="wpdsd_item" name="wpdsd_item_1" value="3m USB Type-C">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_1" placeholder="Enter Qty" oninput="onInputChangeHandler(this)"> $
<input type="number" class="wpdsd_price" name="wpdsd_price_1" step="0.01" value="5.52" oninput="onInputChangeHandler(this)"> $
<span class="wpdsd_total" id="wpdsd_total_1">0.00</span><br>
</div>
<div>
<input type="hidden" class="wpdsd_ID" name="wpdsd_ID_2" value="76">
<input type="text" class="wpdsd_item" name="wpdsd_item_2" value="Wall Charger">
<input type="number" class="wpdsd_qty" name="wpdsd_qty_2" placeholder="Enter Qty" oninput="onInputChangeHandler(this)"> $
<input type="number" class="wpdsd_price" name="wpdsd_price_2" step="0.01" value="4.69" oninput="onInputChangeHandler(this)"> $
<span class="wpdsd_total" id="wpdsd_total_2">0.00</span><br />
</div>
<input type="hidden" name="wpdsd_number_of_lines" value="3">
Take a note that this is much easier using a framework that will take care of data binding automagically, but you would have to start using it from the gecko. Consider taking a look at AngularJS or the newer Angular.
Upvotes: 1