The code that you’re starting with has several improvements that can be made.
Here’s a link to the code that you’re starting with: https://jsfiddle.net/pmw57/a3ky2nvu/
To divide by the right number, we need to know the number of fields that have values in them.
Get the count
We can start of rather simply by adding values to a total variable, and increase a count variable. That lets us then figure out total / count.
var total = 0;
var count = 0;
var test1 = document.getElementById('test1').value;
if (test1) {
total += Number(test1);
count += 1;
}
var test2 = document.getElementById('test2').value;
if (test2) {
total += Number(test2);
count += 1;
}
var test3 = document.getElementById('test3').value;
if (test3) {
total += Number(test3);
count += 1;
}
var average = document.getElementById('average');
average.value = total / count;
The above code is found at https://jsfiddle.net/pmw57/a3ky2nvu/1/
There is room for improvement here though.
Use a loop
You can use a loop to help reduce the amount of duplication.
for (var i = 1; i <= 3; i += 1) {
var test = document.getElementById('test' + i).value;
if (test) {
total += Number(test);
count += 1;
}
}
// var test1 = document.getElementById('test1').value;
// if (test1) {
// total += Number(test1);
// count += 1;
// }
// var test2 = document.getElementById('test2').value;
// if (test2) {
// total += Number(test2);
// count += 1;
// }
// var test3 = document.getElementById('test3').value;
// if (test3) {
// total += Number(test3);
// count += 1;
// }
The above code is found at https://jsfiddle.net/pmw57/a3ky2nvu/2/
That is a simple ES3 way of doing things.
It can also be done by using an array, which helps to open up other benefits.
Use an array
By putting the values into an array we can easily get the number of values, and use a sum function to add them together.
var function add(values) {
var total = 0;
for (var i = 0; i < values.length; i += 1) {
total += Number(values[i]);
}
return total;
}
...
var values = [];
// var totals = 0;
// var count = 0;
for (var i = 1; i <= 3; i += 1) {
var test = document.getElementById('test' + i).value;
if (test) {
values.push(test);
// total += Number(test);
// count += 1;
}
}
var average = document.getElementById('average');
var total = add(values);
average.value = total / values.length;
// average.value = total / count;
The above code is found at https://jsfiddle.net/pmw57/a3ky2nvu/3/
That’s well and good for ES3, but ES5 brings array methods that can be to our benefit.
Use forEach instead of loops
For loops have several problems, so the forEach loop is used instead to help reduce the number of issues.
values.forEach(function (value) {
total += Number(value);
});
// for (var i = 0; i < values.length; i += 1) {
// total += Number(values[i]);
// }
...
var inputs = document.querySelectorAll("input[type=number]");
inputs.forEach(function (input) {
if (input.value) {
values.push(input.value);
}
});
// for (var i = 1; i <= 3; i += 1) {
// var test = document.getElementById('test' + i).value;
// if (test) {
// values.push(test);
// }
// }
Because of the forEach loops, the code now doesn’t need to be updated when the number of inputs are change.
We can also remove the unique identifiers from the form inputs, as they are no longer needed.
Math: <input type="number">
<input type="number">
<input type="number">
<!-- Math: <input type="number" id="test1">
<input type="number" id="test2">
<input type="number" id="test3">-->
The above code is found at https://jsfiddle.net/pmw57/a3ky2nvu/4/
Use functions to simplify the code
Other benefits from using ES5 techniques is that they help to make the code more expressive. To get the total we want to get the input values and filter them for meaningful numbers. That can be expressed purely as code, with:
var values = inputs.map(value).map(toNumber).filter(isNumber)
That combined with a function called mean, that averages the values, helps to make the code easier to understand.
function value(input) {
return input.value;
}
function toNumber(str) {
return Number(str);
}
function isNumber(val) {
return Boolean(val);
}
var inputs = document.querySelectorAll("input[type=number]");
var values = Array.from(inputs).map(value).map(toNumber).filter(isNumber);
// var values = [];
// var inputs = document.querySelectorAll("input[type=number]");
// inputs.forEach(function (input) {
// if (input.value) {
// values.push(input.value);
// }
// });
We can also use a proper function called mean, to calculate the mean average of the values.
function mean(values) {
var sum = 0;
values.forEach(function(value) {
sum += value;
});
return values.length ? sum / values.length : 0;
}
// function add(values) {
// var total = 0;
// values.forEach(function (value) {
// total += Number(value);
// });
// return total;
// }
...
average.value = mean(values);
// var total = add(values);
// average.value = total / values.length;
The updated code is found at https://jsfiddle.net/pmw57/a3ky2nvu/6/
Reduce code in event functions, and separate long lines
The functions in the event function don’t need to be in there. Moving them out of the function makes it easier to understand what the function is supposed to be doing.
function value(input) {
return input.value;
}
function toNumber(str) {
return Number(str);
}
function isNumber(val) {
return Boolean(val);
}
document.getElementById('calcBtn').addEventListener('click', function() {
// function value(input) {
// return input.value;
// }
// function toNumber(str) {
// return Number(str);
// }
// function isNumber(val) {
// return Boolean(val);
// }
...
});
And, instead of spreading out code across a long line, it can be clearer when placed on separate lines instead.
var values = Array.from(inputs)
.map(value)
.map(toNumber)
.filter(isNumber)
);
// var values = Array.from(inputs).map(value).map(toNumber).filter(isNumber);
The above code improvements are found at https://jsfiddle.net/pmw57/a3ky2nvu/7/
Use a getValues function too
Moving the code to get values outside of the event function, helps to make the code in there easier to understand too.
function getValues(inputs) {
return Array.from(inputs)
.map(value)
.map(toNumber)
.filter(isNumber);
}
...
var values = getValues(inputs);
// var values = Array.from(inputs)
// .map(value)
// .map(toNumber)
// .filter(isNumber);
The event function now gets the inputs and the average fields, gets the values from the inputs, and figures out the average value.
document.getElementById('calcBtn').addEventListener('click', function() {
var inputs = document.querySelectorAll("input[type=number]");
var average = document.getElementById('average');
var values = getValues(inputs);
average.value = mean(values);
});
That’s nice and simple now.
The above code update is found at https://jsfiddle.net/pmw57/a3ky2nvu/7/
Use ES6 arrow notation
The arrow notation helps to reduce the visual size of simple functions.
const value = input => input.value;
const toNumber = str => Number(str);
const isNumber = val => Boolean(val);
// function value(input) {
// return input.value;
// }
// function toNumber(str) {
// return Number(str);
// }
// function isNumber(val) {
// return Boolean(val);
// }
And to help inform people that we are using ES6 code, I should use const and let instead of var in other parts of the code too.
function mean(values) {
let sum = 0;
// var sum = 0;
...
}
...
document.getElementById('calcBtn').addEventListener('click', function() {
const inputs = document.querySelectorAll("input[type=number]");
const average = document.getElementById('average');
const values = getValues(inputs);
// var inputs = document.querySelectorAll("input[type=number]");
// var average = document.getElementById('average');
// var values = getValues(inputs);
average.value = mean(values);
});
The above updated code is found at https://jsfiddle.net/pmw57/a3ky2nvu/8/
Improving code by removing let
The let keyword is used instead of const, when values need to change.
It’s not good for values to change as errors can easily occur, so removing such occurrences results in better code.
Instead of the mean function using a sum variable, we can use the ES5 reduce method to add things together instead.
const sum = values.reduce(function(total, value) {
return total + value;
});
// let sum = 0;
// values.forEach(function(value) {
// sum += value;
// });
We can also use arrow notation to simplify the above code too:
const sum = values.reduce(
(total, value) => total + value
);
// const sum = values.reduce(function(total, value) {
// return total + value;
// });
The above improvements are found in the code at https://jsfiddle.net/pmw57/a3ky2nvu/9/
Conclusion
The final scripting that we end up with is:
function mean(values) {
const sum = values.reduce(
(total, value) => total + value
);
return values.length ? sum / values.length : 0;
}
const value = input => input.value;
const toNumber = str => Number(str);
const isNumber = val => Boolean(val);
function getValues(inputs) {
return Array.from(inputs)
.map(value)
.map(toNumber)
.filter(isNumber);
}
document.getElementById('calcBtn').addEventListener('click', function() {
const inputs = document.querySelectorAll("input[type=number]");
const average = document.getElementById('average');
const values = getValues(inputs);
average.value = mean(values);
});
Summary
The above sets of code helps to demonstrate how code is modified and adjusted from simple beginnings, to result in code that is more capable of handling a wider range of situations, while also retaining an easy to understand nature about it.