Get number of edited inputs

Scenario

Every semester my students need to take at least one science, one physics and one history test. The following form gives the right average grades as well as the final grade of a student:

<form>
  Science: <input type="number" id="scienceTest1">
  <input type="number" id="scienceTest2">
  <input type="number" id="scienceTest3">
  <output id="scienceAverage"></output>
  <br> Physics: <input type="number" id="physicsTest1">
  <input type="number" id="physicsTest2">
  <input type="number" id="physicsTest3">
  <output id="physicsAverage"></output>
  <br> History: <input type="number" id="historyTest1">
  <input type="number" id="historyTest2">
  <input type="number" id="historyTest3">
  <output id="historyAverage"></output>
  <br>
  <input type="button" value="Calculate" id="calcBtn">
  <output id="finalGrade"></output>
</form>
document.getElementById('calcBtn').addEventListener('click', function() {
  var scienceTest1 = document.getElementById('scienceTest1').value;
  var scienceTest2 = document.getElementById('scienceTest2').value;
  var scienceTest3 = document.getElementById('scienceTest3').value;
  var physicsTest1 = document.getElementById('physicsTest1').value;
  var physicsTest2 = document.getElementById('physicsTest2').value;
  var physicsTest3 = document.getElementById('physicsTest3').value;
  var historyTest1 = document.getElementById('historyTest1').value;
  var historyTest2 = document.getElementById('historyTest2').value;
  var historyTest3 = document.getElementById('historyTest3').value;
  var scienceAverage = document.getElementById('scienceAverage');
  var physicsAverage = document.getElementById('physicsAverage');
  var historyAverage = document.getElementById('historyAverage');
  var finalGrade = document.getElementById('finalGrade');
  scienceAverage.value = (Number(scienceTest1) + Number(scienceTest2) + Number(scienceTest3)) / 3;
  physicsAverage.value = (Number(physicsTest1) + Number(physicsTest2) + Number(physicsTest3)) / 3;
  historyAverage.value = (Number(historyTest1) + Number(historyTest2) + Number(historyTest3)) / 3;
  finalGrade.value = (scienceAverage.value * 5 + physicsAverage.value * 3 + historyAverage.value * 2) / 10;
});

The problem is it only works if all the fields are edited. If the student doesn’t take some tests, the average grades won’t show the correct values. I know it’s because of dividing by the fixed number 3 when it calculates the average grades:

scienceAverage.value = (Number(scienceTest1) + Number(scienceTest2) + Number(scienceTest3)) / 3;
physicsAverage.value = (Number(physicsTest1) + Number(physicsTest2) + Number(physicsTest3)) / 3;
historyAverage.value = (Number(historyTest1) + Number(historyTest2) + Number(historyTest3)) / 3;

Question

What is a simple approach to get the number of non-empty input fields in each row?

Note: It’s just a sample form and the actual number of courses can be more than that, so I need a generic code.

Firstly, welcome to the forums.

While we are primarily here to help people with their Free Code Camp progress, we are open to people on other paths, too. Some of what you are asking is pretty trivial in the Free Code Camp context, so you might find that if you’re not getting the instruction and material you need in your current studies, the FCC curriculum will really help you get started. At a modest guess I’d say investing a 4-5 hours working through the curriculum here will really pay off. You can find the curriculum at https://freecodecamp.org.

With your current questions, we don’t have enough context to know what you already know or don’t know, so it is impossible to guide you without just telling you the answer (which we won’t do).

It is pretty typical on here for people to share a codepen / jsfiddle example of what they have tried so that anyone helping has more of an idea of what help is actually helpful.

Please provide some example of what you’ve tried and I’m sure you’ll get more help.

Happy coding :slight_smile:

Dear Ariel,

Thanks for the warm greeting!
I’ve already provided all details, but I can’t share a JSFiddle link because of your forum limitation, i.e. newcomers can’t post links.

Just post the path part of the URL, i.e. the part after jsfiddle.net

Demo on JSFiddle: Mori/p91bw7qz

You can mark all the inputs as required, and on submit of your form you use checkValidity to check before calculating the average.

Something like (pseudocode):

//onSubmit
function() {
 // checkValidity return true or false
 if(form.checkValidity()) {
   // true
   calculateAverage()
 } else {
   displayError()
  }
}

Otherwise you can simply select all your input in a NodeList array

const n = document.querySelectorAll('input[type="number"]');

Then use any valid method to loop over the array and look for any empty value === ""

This for example will log true if input is empty

const n = document.querySelectorAll('input[type="number"]');

n.forEach(i => console.log(i.value === '') );

Hope this give you some inspiration :slight_smile:

As I said it’s a sample form. The script should be fixed, but the HTML should be flexible so I can add as many courses as I wish.

Yes.

You’re right, but it gives chances to those who didn’t score highly. Anyway, the language school rules are out of my control.

I can’t: A student doesn’t have to take all the tests.

for (var input of inputs) {
  if (input.value === '') {
    // Do what?
  }
}

That’s what I don’t know.

Well, but that depends on what is your ultimate goal… we cannot tell that for you.

If you wanted to calculate the average of N valid input, I would:

1 - assign a unique identifier to the same “type” of inputs. For example a data-type

<input type="number" id="scienceTest1" data-type="science">

2 - select the type, then filter out the invalid one

const s = document.querySelectorAll('input[data-type="science"]');
const valid = Array.prototype.filter.call(s, i => i.value);

At this point is fairly trivial to get the average based on how many entries are valid

  if(valid.length) {
    const avg = valid.reduce((acc, curr) => acc + Number(curr.value), 0) / valid.length
    // do what you want with you average now
  }

Does it make sense?

EDIT for clarity:
This way you can get the average of only a specific sets of inputs, so you can display that individually.

1 Like