Forum Moderators: open

Message Too Old, No Replies

Get the number of edited inputs

         

Rain_Lover

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

10+ Year Member Top Contributors Of The Month



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?

topr8

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

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



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

Rain_Lover

2:03 pm on Mar 5, 2019 (gmt 0)

10+ Year Member Top Contributors Of The Month



Good idea, but I don't know how to do it.

topr8

8:46 pm on Mar 5, 2019 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



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;
});

NickMNS

9:54 pm on Mar 5, 2019 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



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.

Rain_Lover

3:52 am on Mar 6, 2019 (gmt 0)

10+ Year Member Top Contributors Of The Month



@NickMNS: Thanks for the answer, but I'd rather use the older version of JavaScript.

lucy24

6:36 am on Mar 6, 2019 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



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++)

topr8

1:02 pm on Mar 6, 2019 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



>>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.

NickMNS

2:47 pm on Mar 6, 2019 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



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++)

Rain_Lover

5:12 pm on Mar 6, 2019 (gmt 0)

10+ Year Member Top Contributors Of The Month



Here's an attempt: [jsfiddle.net...]
I wonder if you see any problems or any way to improve it.

Rain_Lover

4:47 pm on Mar 9, 2019 (gmt 0)

10+ Year Member Top Contributors Of The Month



Here's the final update: [jsfiddle.net...]

NickMNS

4:58 pm on Mar 9, 2019 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Looks good!

Rain_Lover

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

10+ Year Member Top Contributors Of The Month



Thanks for reviewing!