Noob Javascript auto total - javascript

Apologies for what I'm sure is a rather simple javascript question, but how would I get the following total funtion to calculate the total every time a cost is entered into the table, i.e. without having to press the total button to submit the form.
function totalIt() {
var qtys = document.getElementsByName("qty[]");
var total=0;
for (var i=1;i<=qtys.length;i++) {
calc(i);
var price = parseFloat(document.getElementById("price"+i).value);
total += isNaN(price)?0:price;
}
document.getElementById("total").value=isNaN(total)?"0.00":total.toFixed(2);
}
http://jsfiddle.net/mplungjan/jDfFU/

IF the qty fields are where the costs are being entered, then in your setup code, just hook totalIt up to the change (and possibly input, if you want immediate feedback) events:
var qtys = document.getElementsByName("qty[]");
var i;
for (i = 0; i < qtys.length; ++i) {
qtys[i].addEventListener("change", totalIt, false);
qtys[i].addEventListener("input", totalIt, false);
}
On modern browsers, if you hook input, you don't need to hook change; the above just allows for the possibility of older browsers by hooking change in case they don't support input.

Related

Optimization of a large multiselect list

I have three MultiselectLists
Countries which has 22 values
Cities which has ~800 values
Sites which has ~1700 values
I am using the jquery bootstrap-multiselect library
It is fast enough for what I am trying to do that the user doesn't notice any overhead except in one case when the user selects USA 600 Cities and 1200-1300 sites get checked/selected this takes about 30 seconds in IE, and about 6 seconds in Firefox which is enough for a reasonable person to believe that something is wrong with the page.
I don't want to change the UI of the website at all since this works beautifully in every other case, including when the user hits a "select-all" or "deselect-all" and just wants to run a full report
these are the two functions that take about 90-95% of the time to execute
getInputByValue: function(value) {
var checkboxes = $('li input', this.$ul);
var valueToCompare = value.toString();
for (var i = 0; i < checkboxes.length; i = i + 1) {
var checkbox = checkboxes[i];
if (checkbox.value === valueToCompare) {
return $(checkbox);
}
}
}
getOptionByValue: function(value) {
var options = $('option', this.$select);
var valueToCompare = value.toString();
for (var i = 0; i < options.length; i = i + 1) {
var option = options[i];
if (option.value === valueToCompare) {
return $(option);
}
}
}
and this is the code I use to select all of the options, I have a Dictionary>>(); that I pass from my controller into my view that governs the relationship between the Countries/Cities/ and sites
onChange: function (option, checked, select) {
try{
if (checked == true) {
var t0 = performance.now();
$('#Cities').multiselect('select', Object.keys(CountryCitySite[$(option).text()]),'triggeronChange');
var t1 = performance.now();
list = Object.keys(CountryCitySite[$(option).text()])
var t2 = performance.now();
for(var i = 0; i<list.length; i++)
{
$('#Sites').multiselect('select', CitySite[list[i]])
}
var t3 = performance.now();
}
else if (checked == false) {
$('#Cities').multiselect('deselect', Object.keys(CountryCitySite[$(option).text()]), 'triggeronChange');
list = Object.keys(CountryCitySite[$(option).text()])
for(var i = 0; i<list.length; i++)
{
$('#Sites').multiselect('deselect', CitySite[list[i]], 'triggeronChange')
}
}
}
Some thoughts:
1. maybe in the case of USA I could do an Ajax, and post everything to the server and then return three new selectlists with the appropriate options checked? the problem with this is that I don't see this taking less than like 7 or 8 seconds if not more which is still much too long
var options = $('option', this.$select);
this Jquery selection is several orders of magnitude slower than just using native javascript Document.getelementsbytagname, since I know that all of the checkboxes have unique values maybe I could replace this Jquery selection with native javascript and get all of the checkboxes that way
as a 'hack' I could send two invisible multiselectlists at the very beginning with all of the boxes for USA checked, these invisible multiselects behave exactly the same as the visible ones in most respects but if the user selects USA, these are shown instead of the originals. This actually does work, it makes the website a little slower but since the selection of every other option is so quick it doesn't really matter, it seems like a sub-par solution but this is currently the best I have
if anyone thinks of anything else or can give any advice on this I would be very appreciative
Sincerely Josh

Use the same function on multiple gridviews/tables

I have 6 asp gridviews that need the same calculations done on each of them. I could hard code the function 6 times over but looking for a more efficient way of doing it.
What I'm doing: There are 3 input boxes on each row, of each gv and I need to calculate the average and send it to a lbl in the last column.
Here is what I've done for the first gv:
function calculate() {
//********************
//Development Grid
//********************
//Find the number of rows in the grid
var rowCount = $('#devGV tr').length;
//Iterate through each row looking for the input boxes
for (var i = 0; i < rowCount; i++) {
//convert the total variable to an int
var total = 0;
parseInt(total);
//This variable is for tracking the number of actual fields that are populated.
//Not all the text fields will always be needed, so the average will not always be calculated by dividing by 3
var averNum = 0;
//Iterate through each text box
$("#devGV tr:eq(" + i + ")").find(":input[type=text]").each(function () {
//Convert the value to an int
var thisValue = parseInt($(this).val());
//In the value is blank, change it to 0 for calculation purposes
if (isNaN(thisValue) || thisValue == 0) {
thisValue = 0;
}
else {
averNum += 1;
}
total = (total + thisValue);
});
//Populate the totals label for each row
total = total / averNum;
total = total.toFixed(2);
//In the value is blank, change it to 0 for calculation purposes
if (isNaN(total)) {
total = 0;
}
$("#devGV tr:eq(" + i + ")").find("[class$='RowTotals']").text(total);
}
}
The above function is being trigger by 'onBlur' on each of the text fields. Is there a way I can make this block work for all the gridviews? I'm sure its just a matter of updating the selectors but I'm at a loss on how to do that.
Simplest is probably to pass a jquery object to calculate function:
function calculate(gv) {
Then use .find() in place of where you have the ID, for instance like this for the "find the number of rows in the grid":
var rowCount = gv.find('tr').length;
The reason I say it's easier to pass a jQuery object into the function (rather than the string ID of it, say) is that it lets you do something like this:
$('.some-gridview-css-class').each(function() {
calculate($(this));
});
Obviously replace that selector with whatever selector will identify your 6 gridviews for you.
EDIT: Oh I didn't read carefully enough. You want to do it onblur of a textbox. That means you want something like this:
$(document).ready(function() {
$(body).on('blur', 'input-fields-selector', function() {
calculate($(this).closest('gridview-selector'));
});
});
You have to replace input-fields-selector and gridview-selector with selectors to find the appropriate fields (which will depend on your HTML).

how to disable onclick function once it is less than another variable?

Hello everyone and thanks for any help in advance,
I have some functions already in place, first one adds 1% of the total money amount once every second. the second is onclick there is $100 added to the total original amount. The third is to minus $200 plus 10% of the 200 from the total amount, so every time it is clicked the minus amount goes up.
These functions are working on my local machine but I am unable to get them to work in a JSFIDDLE.
I want to disable the minus button once the total amount is less than the minus amount, and also enable it again once there is enough money in the total amount to use.
Here is my JS so far
setInterval(function () {
var moneyTotal = document.getElementById('money-total').innerHTML;
var perSec = (1 / 100) * moneyTotal;
var moneyNewTotal = (+moneyTotal) + (+perSec);
document.getElementById('money-total').innerHTML = (moneyNewTotal.toFixed(2));
document.getElementById('per-second').innerHTML = (perSec.toFixed(2));
}, 1000);
function add() {
var addAmount = 100;
var moneyTotal = document.getElementById('money-total').innerHTML;
var addTotal = (+addAmount) + (+moneyTotal);
document.getElementById('money-total').innerHTML = (addTotal.toFixed(2));
}
function minus() {
var cost = document.getElementById('cost').innerHTML;
var money = document.getElementById('money-total').innerHTML;
if (money > cost) {
var moneyNewTotal = (+money) - (+cost);
var newCost = (10 / 100) * cost;
var costTotal = (+newCost) + (+cost);
document.getElementById('cost').innerHTML = Math.ceil(costTotal);
document.getElementById('money-total').innerHTML = (moneyNewTotal.toFixed(2));
} else {
alert("Not enough money");
}
}
Any help would be greatly appreciated!
First, the Fiddle isn't working because you have it set to load your javascript onLoad which is wrapping your functions in a different scope and so they aren't available on the window. Changing it to load the javascript No wrap - in <head> (or <body>) will help it work.
Then you need to have something that is checking for the value and enabling disabling the minus button.
function checkTotal() {
var moneyTotal = +document.getElementById('money-total').innerHTML;
var cost = +document.getElementById('cost').innerHTML;
if (cost > moneyTotal) {
document.getElementById('minus').setAttribute('disabled', 'disabled');
} else {
document.getElementById('minus').removeAttribute('disabled');
}
}
Then you would need to call this at the end of the add, minus, and even the function you have defined in setInterval so that as money gets added it checks. This code only adds the disabled attribute to the button, so any styling would need to be handled either by the code or through CSS and setting/removing class names or something similar.
There are perhaps different ways to do this through data binding or using an event model as part of a framework, but those are a more substantial topic that can't really be covered in this answer.

JavaScript using isNaN to validate returns NaN

I have some code here that will make validations of whether or not the input from a text box is NOT an empty string and isNaN. When i do these validations on amounts entered, i would like it to add them up.. however when a user does not enter anything in one or more amount fields the program should just add entered fields. But instead i get NaN showing in the total field.
link to full code: http://jsfiddle.net/KxNqQ/
var $ = function (id) {
return document.getElementById(id);
}
var calculateBills = function () {
var myErrorFlag = "N";
for (i = 1; i <= 4; i++) {
AmountNumber = 'amount' + i;
AmountValue = $(AmountNumber).value;
if (AmountValue != "" && isNaN(AmountValue)) {
$(AmountNumber).style.color = "red";
myErrorFlag = "Y";
} else {
$(AmountNumber).style.color = "black";
myErrorFlag = "N";
}
}
if (myErrorFlag != "Y") {
var Amount = 0;
for (i = 1; i <= 4; i++) {
Amount += parseInt($('amount' + i).value,10);
}
$('total').value = Amount;
}
}
var clearFields = function () {
for (i = 1; i <= 4; i++) {
itemName = 'item' + i;
$(itemName).value = "";
}
for (i = 1; i <= 4; i++) {
amountName = 'amount' + i;
$(amountName).value = "";
}
$('total').value = "";
}
window.onload = function () {
$("clearfields").onclick = clearFields;
$("addbills").onclick = calculateBills;
}
I think you've got your requirements a little bit confused, or at the very least I was confused by them. So in order to answer your question, I'm going to rephrase the requirements so I understand them better. This is a useful exercise that I try to do when I'm not 100% sure of the requirements; if I can't get the requirements right, what's to say I'll get the code right?
So the requirements – as I understand them – are:
Given each amount input
When the input has a value
And that value is a number
Then add the value to the total
And make the input color black
But if the input does not have a value
Or that value is not a number
Then make the input color red
Going through your code, I can see a number of problems with it. First, I noticed that both AmountNumber and AmountValue are global variables, because they were not declared local with the var keyword. So before fixing our code, let's change that. Let's also change the variable names to something that more accurately describe what they are, hopefully making the code easier to understand:
var input = $('amount' + i);
var value = input.value;
Now, note that I chose to store the element in the input variable. This is so we don't have to look it up multiple times within the loop. Looking things up in the DOM can be expensive so we'll want to keep it to a minimum. There are other was to look up elements as well, such as getElementsByClassName, querySelector and querySelectorAll; those are left as an exercise for the reader to research and evaluate.
Next, in each iteration of the loop, you check that AmountValue is not a string and simultaneously is not a number:
if (AmountValue != "" && isNaN(AmountValue)) {
This will be true so long as AmountValue is truthy (which is the case for non-empty strings) and so long as isNaN thinks it's a number (which is the case for strings that contain numbers.) It really is rather confusing; if I understand your code correctly this clause is there to check for invalid input and if it is true should mark the input field red and set a flag. I.e. this is the but clause in the aforementioned requirements.
Let's rewrite this to be the when clause instead, we'll take care of the but later. Before we do that, let's look at the myErrorFlag. It's used – I think – to see whether all input is well formed and in that case, add it all up. Well, validation and summation can be done in one fell swoop, so let's get rid of the flag and sum the values while validating them. So we replace myErrorFlag with a total variable:
var total = 0;
Now, let's get back to our clause. The requirements say:
When the input has a value
And that value is a number
Then add the value to the total
In code, that should look something like this:
if (value && !isNaN(value)) {
total += parseInt(value, 10);
input.style.color = 'black';
}
There are a couple of things going on here. For one, the if statement has been turned on its head a bit from what it was. It first checks to see that value is truthy, then that it is a number. The second check can be a bit tricky to read, because it is essentially a double negation; in english it reads "is not not a number", i.e. "is a number". I'll leave it as an exercise for the reader to figure out whether there's a more easily understood way of writing this check.
Now what about the but clause in our requirements?
But if the input does not have a value
Or that value is not a number
Then make the input color red
Well, it's essentially the inverse of our previous statement, so let's simply add an else clause:
else {
input.style.color = 'red';
}
Because the requirements doesn't mention the total variable in this clause, it is simply ignored and doesn't show up in the end result.
Adding it all up (no pun intended) the code – with comments – looks like this:
var calculateBills = function () {
var total = 0;
for (i = 1; i <= 4; i++) {
// Given each amount input
var input = $('amount' + i);
var value = input.value;
if (value && !isNaN(value)) {
// When the input has a value
// And that value is a number
// Then add the value to the total
total += parseInt(value, 10);
// And make the input color black
input.style.color = 'black';
} else {
// But if the input does not have a value
// Or that value is not a number
// Then make the input color red
input.style.color = 'red';
}
}
$('total').value = total;
};
There are more things that could be learned from this to make for better code. For instance, this code will break if the number of inputs change, or if their id names change. This is because they are selected specifically by their IDs and as such, if those change then this code will no longer function.
Another potential issue is that we're setting inline styles on the inputs as we loop over them. This means that in order to keep this code up to date with the styling of the site, it'll have to change. Generally, mixing styling and functionality like this is not a good idea and should be avoided. One way of doing so is to use class names instead, and toggle these on and off. Incidentally, this could also help the previous problem I mentioned.
There are other problems as well, but we'll leave those for another day. Hope this helps!
Try this
var calculateBills = function () {
var Amount = 0;
for (i = 1; i <= 4; i++) {
var AmountElement = $('amount' + i),
AmountValue = AmountElement.value;
if (AmountValue != "" && !isNaN(AmountValue)) {
AmountElement.style.color = "red";
Amount += parseInt(AmountValue,10);
} else {
AmountElement.style.color = "";
}
}
$('total').value = Amount;
};
Demo
Anyway, instead of using elements with id like id="amount1", id="amount2", id="amount3", etc., you could use classes (e.g class="amount") and get them with .getElementsByClassName

JavaScript validation, prevent duplicate input fields

I have a form with 10 Select Lists all have the same items. The items are populated from a PHP/MySQL array. The user needs to select one item per select list. I need to prevent the user from selecting the same item twice before submitting the form.
function checkDropdowns(){
var iDropdowns = 10;
var sValue;
var aValues = new Array();
var iKey = 0;
for(var i = 1; i <= iDropdowns; ++i){
sValue = document.getElementById('test' + i).value;
if ( !inArray(sValue, aValues) ){
aValues[iKey++] = sValue;
}else{
alert('Duplicate!');
return false;
}
}
return true;
}
Use javascript to add an event listener on the value change of the selects. That function would then loop through the selects taking the values into memory after having compared it to the values it had already. If the loop finds that the current select has an option that is already selected, put it back to default value and display a little message...
Or, still on a change event, take the value of the just selected item and remove all the items of this value in the 10 selects. So at the end the user will only have 1 choice, since he only sees the options he can choose. But be careful, if the user changes his mind on one select, make sure you add back the option you removed in the first place.
Option 2 is to be prefered as a user point of view, you will cause less frustration.
EDIT:
The code you are providing already does quite a lot... All you need now is something to revert the change if it is invalid:
var defaultValues = [];
function onLoadSelect(){//Please execute this on document load, or any event when the select are made available.
var iDropdowns = 10;
var iKey = 0;
for(var i = 1; i <= iDropdowns; ++i){
var sValue = document.getElementById('test' + i).value;
defaultValues['test' + i] = sValue;
}
}
Then, in your function's else, reset the value according to the defaults we have gathered:
else{
alert('Duplicate!');
document.getElementById('test' + i).value = defaultValues['test' + i];
return false;
}
I have written code, i think it can be improved but it works as you asked.
Put it in inside script tag under body so it loads after document.
Put id names of select/dropdown elements in id array.
Take a look: //took me 3 hours O_O
http://jsfiddle.net/techsin/TK9aX/15/
i think i need better strategy to approach programming.

Categories

Resources