function doCalc(el) {
price = document.getElementById("price").value;
console.log("price", price);
el.total.value = (price * el.quantity.valueAsNumber * el.wattage.valueAsNumber / 1000 * (el.minutes.valueAsNumber / 60) / 100).toFixed(2);
}
Don't forget to use var
when declaring variables. Not doing so will cause JS to declare it in the global namespace, which anyone can clobber. There may even be a price
global existing already, and you just replaced its value.
Use the browser debugger and plant breakpoints in the source code. It's better than peppering your code with console
functions.
Notice that your HTML is littered with JS. Use addEventListener
instead of inlining your event handlers. Keep JS just JS, and HTML just HTML.
function addRow() {
var itm = document.getElementById("template");
var cln = itm.cloneNode(true);
document.getElementById("repeatafterme").appendChild(cln);
}
The problem I see with cloneNode
is that it carries over some state from the cloned nodes, which can be a problem.
<script type="text/template id="template">
<button onclick="addRow();">Add row</button>
...
</script>
var container = document.createElement('div');
var template = document.getElementById('template').innerHTML;
container.innerHTML = template;
container.getElementByClassName('some-input')[0].value = '';
somewhere.appendChild(container);
One solution would be to create an empty <div>
using document.createElement
, and populate it using innerHTML
with a string form of the template. Look for the elements whose values need replacing, then append that div to the DOM.
function grandTotal() {
t = document.getElementsByName("total");
gt = 0.0;
for (i = 0; i < t.length; i++) {
console.log(t[i].value);
gt = parseFloat(gt) + parseFloat(t[i].value);
}
console.log("Grand total:", gt);
document.getElementById("grandtotal").value = gt.toFixed(2);
}
- Same as before, don't forget
var
.
- Same as before, don't use
console
.
- When
parseFloat
receives an input that isn't starting with a number, it will return NaN
. Further math with NaN
will make the results NaN
. You should always check the result of string-to-number conversion functions.
In the real world, nobody uses vanilla JS for this. I suggest you start looking into frameworks like Angular to do this repetitive task for you. With most frameworks, you only have to worry about operating with data and templates, and the frameworks do the heavy lifting.
// You worry only here. Angular binds the data so changes in the input
// automatically reflect in the object made as the model.
$scope.rows = [{ id: 1, name: 'washington', value: 19.99 }, ...];
$scope.grandTotal= function(){
return $scope.rows.reduce(function(partial, value){
return partial + value;
}, 0);
}
// You don't have to muck around with DOM
<label>Grand Total</label><span>{{ grandTotal() }}</span>
<table ng-repeat="row in rows">
<tr>
<td><input type="text" model="row.id"></td>
<td><input type="text" model="row.name"></td>
<td><input type="text" model="row.value"></td>
</tr>
</table>
Moving down closer to lower-level code. In PHP, you can represent a nested, repetitive structure with some trickery in the form name.
<input type="text" name="foo[]" value="1">
<input type="text" name="foo[]" value="2">
<input type="text" name="foo[]" value="3">
// Becomes ["1", "2", "3"]
<input type="text" name="foo[a]" value="1">
<input type="text" name="foo[b]" value="2">
<input type="text" name="foo[c]" value="3">
// Becomes ["a" => "1", "b" => "2", "c" => "3"]
<input type="text" name="foo[0][a]" value="1">
<input type="text" name="foo[0][b]" value="2">
<input type="text" name="foo[1][a]" value="3">
<input type="text" name="foo[1][b]" value="4">
// Becomes [["a" => "1", "b" => "2"], ["a" => "3", "b" => "4"]]
We can do the same thing in JS with a few tools. First, we can use Mustache to render the template in the same manner. To get the values in their nested form, you can use the serializeObject plugin.
// var rows = [{ id: 1, name: 'washington', value: 19.99 }, ...];
// Mustache.render(template, { rows: rows });
<form name="item-form">
<table>
{{# rows }}
<tr>
<td><input type="text" name="items[{{ id }}][id]"/></td>
<td><input type="text" name="items[{{ id }}][name]"/></td>
<td><input type="text" name="items[{{ id }}][value]"/></td>
</tr>
{{/ rows }}
</table>
</form>
// var data = $('form[name="item-form"]').serializeObject();
// { items: [{ id: 1, name: 'washington', value: 19.99 }, ...], }