Welcome to WebmasterWorld Guest from 34.229.24.100

Forum Moderators: open

Get the number of edited inputs

     
1:25 pm on Mar 5, 2019 (gmt 0)

Full Member

5+ Year Member Top Contributors Of The Month

joined:Mar 1, 2010
posts: 209
votes: 0


Scenario

Every semester my students need to take at least one test. The following form gives the right average grade of a student:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Average Grade</title>
</head>
<body>
<form>
Math: <input type="number" id="test1">
<input type="number" id="test2">
<input type="number" id="test3">
<output id="average"></output>
<br>
<input type="button" value="Calculate" id="calcBtn">
</form>
<script>
document.getElementById('calcBtn').addEventListener('click', function() {
var test1 = document.getElementById('test1').value;
var test2 = document.getElementById('test2').value;
var test3 = document.getElementById('test3').value;
var average = document.getElementById('average');
average.value = (Number(test1)+Number(test2)+Number(test3)) / 3;
});
</script>
</body>
</html>

DEMO [jsfiddle.net]

The problem is it works right only if all the fields are edited. If the student doesn't take some tests, the average grade won't show the right value. I know it's because of dividing by the fixed number 3 when it calculates the average grade:
average.value = (Number(test1)+Number(test2)+Number(test3)) / 3;

Question

What is a simple approach to get the number of changed input fields?
1:58 pm on Mar 5, 2019 (gmt 0)

Senior Member

WebmasterWorld Senior Member topr8 is a WebmasterWorld Top Contributor of All Time 10+ Year Member Top Contributors Of The Month

joined:Apr 19, 2002
posts:3478
votes: 76


couldn't you make the number 3 a variable (divideby) instead with an initial value of zero.

then ...
if test1 > 0 ... add 1 to the value of divideby
if test2 > 0 ... add 1 to the value of divideby
if test3 > 0 ... add 1 to the value of divideby
2:03 pm on Mar 5, 2019 (gmt 0)

Full Member

5+ Year Member Top Contributors Of The Month

joined:Mar 1, 2010
posts: 209
votes: 0


Good idea, but I don't know how to do it.
8:46 pm on Mar 5, 2019 (gmt 0)

Senior Member

WebmasterWorld Senior Member topr8 is a WebmasterWorld Top Contributor of All Time 10+ Year Member Top Contributors Of The Month

joined:Apr 19, 2002
posts:3478
votes: 76


i don't really use js ... something like this ... although i imagine there is a more elegant way of doing it.

document.getElementById('calcBtn').addEventListener('click', function() {
var divideby = 0;
var test1 = document.getElementById('test1').value;
var test2 = document.getElementById('test2').value;
var test3 = document.getElementById('test3').value;
if(test1>0){divideby++;}
if(test2 > 0){divideby++;}
if(test3 > 0){divideby++;}
var average = document.getElementById('average');
average.value = (Number(test1) + Number(test2) + Number(test3)) / divideby;
});
9:54 pm on Mar 5, 2019 (gmt 0)

Senior Member

WebmasterWorld Senior Member Top Contributors Of The Month

joined:Apr 1, 2016
posts:2619
votes: 768


Here is the fiddle updated:
[jsfiddle.net...]

Here is the code:

document.querySelector('#calcBtn').addEventListener('click', function() {
const inputArr = Array.from(document.querySelectorAll('input[type=number]')),
average = document.querySelector('output');
let validScores = [];
inputArr.forEach(function(score){
if(parseInt(score.value) > 0) validScores.push(parseInt(score.value))
});
const count = validScores.length,
scoreSum = validScores.reduce((partial_sum, a) => partial_sum + a);
average.value = scoreSum / count
});


Explanation:
Get each of the inputs as an array. querySelectorAll gets all the inputs but as a node list. Array.from converts the node list to an array. Then I create an empty array to store the valid scores. Using, forEach I loop through the array of inputs and pass their values through a conditional if, in this case > 0 (my need to be something else). If the value meets the condition it is added to the array of valid scores. Then I take the valid scores, count them, sum them and finally divide the sum by the count.

With this approach you can have has as many inputs as needed and still use the same code.

Note: this script uses ECMAScript6 and may not work on older browsers.

I hope this helps.
3:52 am on Mar 6, 2019 (gmt 0)

Full Member

5+ Year Member Top Contributors Of The Month

joined:Mar 1, 2010
posts: 209
votes: 0


@NickMNS: Thanks for the answer, but I'd rather use the older version of JavaScript.
6:36 am on Mar 6, 2019 (gmt 0)

Senior Member from US 

WebmasterWorld Senior Member lucy24 is a WebmasterWorld Top Contributor of All Time 5+ Year Member Top Contributors Of The Month

joined:Apr 9, 2011
posts:15699
votes: 810


if(test2 > 0)
What happens if they took the test but scored 0 on it?

In any case this seems like a good place to use name instead of id. You can then have a group of fields with the same name, no need to decide ahead of time how many there are, and loop through all of them, adding 1 to the value of "divideby" each time. The command is
blahblah = document.getElementsByName("tests")
(note that it becomes Elements, plural, not Element)
and then a loop such as
for (count = 0; count < blahblah.length; count++)
1:02 pm on Mar 6, 2019 (gmt 0)

Senior Member

WebmasterWorld Senior Member topr8 is a WebmasterWorld Top Contributor of All Time 10+ Year Member Top Contributors Of The Month

joined:Apr 19, 2002
posts:3478
votes: 76


>>What happens if they took the test but scored 0 on it?

yes indeed, i was just looking at a very quick simple way to do it - and i don't use js, but no-one else had answered the question.

i guess one could test to see if the field was empty rather than zero.

anyway nick sorted it out properly.
2:47 pm on Mar 6, 2019 (gmt 0)

Senior Member

WebmasterWorld Senior Member Top Contributors Of The Month

joined:Apr 1, 2016
posts:2619
votes: 768


What happens if they took the test but scored 0 on it?

That is why I wrote in my explanation:
(may need to be something else).

There are many conditional checks that could be done, such as checking for length, for null, for empty string etc... >0 was the easiest but not best. @Rain_Lover can select the one that is meets the needs.

As for ECMAScript6 there are really only 2 things that need to change Array.from, and the reduce. Both can be rewritten using a for loop as described by Lucy24
for (i = 0; i < validScores.length; i++)
5:12 pm on Mar 6, 2019 (gmt 0)

Full Member

5+ Year Member Top Contributors Of The Month

joined:Mar 1, 2010
posts: 209
votes: 0


Here's an attempt: [jsfiddle.net...]
I wonder if you see any problems or any way to improve it.
4:47 pm on Mar 9, 2019 (gmt 0)

Full Member

5+ Year Member Top Contributors Of The Month

joined:Mar 1, 2010
posts: 209
votes: 0


Here's the final update: [jsfiddle.net...]
4:58 pm on Mar 9, 2019 (gmt 0)

Senior Member

WebmasterWorld Senior Member Top Contributors Of The Month

joined:Apr 1, 2016
posts:2619
votes: 768


Looks good!
5:20 pm on Mar 9, 2019 (gmt 0)

Full Member

5+ Year Member Top Contributors Of The Month

joined:Mar 1, 2010
posts: 209
votes: 0


Thanks for reviewing!