Уже есть несколько решений.Вот мое.
В вашем коде есть что оптимизировать, и я думаю, что это хорошая идея - динамически создавать HTML на основе простого массива конфигурации, такого как
const subjects = [{
name: 'science',
numberOfTests: 3
}, {
name: 'physics',
numberOfTests: 2
}, {
name: 'history',
numberOfTests: 3
}];
, поэтому, еслименяются темы или количество тестов, вам не нужно менять код, а только эту конфигурацию.Я попытался прокомментировать мой пример кода, чтобы понять, что там делается.Но это не важная часть.Более важно то, что вам нужно понять, что происходит в части расчета, чтобы быть уверенным, что расчет правильный.Итак, я начну с этой части.
Если вы добавите атрибуты name к входам результатов теста и дадите каждому входу одного и того же субъекта одно и то же имя, вы можете легко получить NodeList этих входовпроверьте значения для каждого узла и рассчитайте на его основе.Итак, вы знаете значения и количество тестов, которые студент сдал по каждому предмету.
Давайте посмотрим
/* EventListener for the calculate button */
btn.addEventListener('click', function(e) {
e.preventDefault(); // don't submit the form
var totalAvSum = 0; // var for the total of all subject average totals
/* for all subjects in your configuration array */
subjects.forEach(function(subject) {
/* NodeList of all inputs with Name subject.name+'Test' */
let subjResInputs = document.getElementsByName(subject.name+'Test');
let testTotal = 0; // sum of test results
let testCnt = 0; // number of tests the student took
let tval; // value of input
/* for each input of the subject */
Array.prototype.map.call(subjResInputs, function(t){
tval = (t.value * 1); // make sure, value is treated as number
if (tval > 0) { // only if there is a value
testTotal += tval; // add test result
testCnt += 1; // increase test count
}
});
/* calculate average and show it in output */
totalAvSum += (testTotal/testCnt);
document.getElementById(subject.name+'Average').textContent = (testTotal/testCnt);
});
/* after calculating average per subject show total average */
document.getElementById('totalAverage').textContent = totalAvSum/subjects.length;
});
Я использую document.getElementsByName () здесьполучить NodeList элементов ввода для каждого предмета.Затем я использую array.map () , чтобы просмотреть этот список.Чтобы проверить, имеет ли вход значение, я умножаю значение на 1 (что дает число) и вычисляю только, если результат больше 0.
Остальное - динамический материал для HTML.
/* This is your configuration.
The form will be created based on that configuration.
So you don't need to change anything in the code if
subjects or number of tests change. */
const subjects = [{
name: 'science',
numberOfTests: 3
}, {
name: 'physics',
numberOfTests: 2
}, {
name: 'history',
numberOfTests: 3
}];
/* this functioncreates the form table */
function createFormTable() {
var tr, td, txt, outp, btn, frmTbl;
// frmTbl = document.getElementById('formTable');
/* create table */
frmTbl = document.createElement('table');
frmTbl.setAttribute('id', 'formTable'); // set id to 'formTable'
/* create table head */
tr = document.createElement('tr');
td = document.createElement('th');
txt = document.createTextNode('subject');
td.appendChild(txt);
tr.appendChild(td);
td = document.createElement('th');
txt = document.createTextNode('test results');
td.appendChild(txt);
tr.appendChild(td);
td = document.createElement('th');
txt = document.createTextNode('arith. mean');
td.appendChild(txt);
tr.appendChild(td);
/* add table head to table */
frmTbl.appendChild(tr);
/* create table row for each subject
the table row object is created in function createSubjectRow
and here added to the table */
subjects.forEach(function(subject) {
frmTbl.appendChild(createSubjectRow(subject)); // add tr to table
});
/* row with total average */
/* create tr element */
tr = document.createElement('tr');
td = document.createElement('th'); // td for text total
td.setAttribute('colspan', 2);
td.style.textAlign = 'right';
txt = document.createTextNode('total'); // textNode
td.appendChild(txt); // add textNode to td
tr.appendChild(td); // add td to tr
frmTbl.appendChild(tr); // add tr to table
td = document.createElement('td'); // td for total average output
outp = document.createElement('output'); // create output element
outp.setAttribute('id', 'totalAverage'); // set id
td.appendChild(outp); // add output to td
tr.appendChild(td); // add td to tr
frmTbl.appendChild(tr); // add tr to table
/* button */
btn = document.createElement('button');
btn.setAttribute('id', 'calcBtn');
txt = document.createTextNode('calculate');
btn.appendChild(txt);
// document.getElementById('gradesForm').appendChild(btn);
/* add button to last row in table */
tr = document.createElement('tr');
td = document.createElement('th'); // td for button
td.setAttribute('colspan', 3);
td.appendChild(btn); // add button to td
tr.appendChild(td); // add td to tr
frmTbl.appendChild(tr); // add tr to table
/* EventListener for the calculate button */
btn.addEventListener('click', function(e) {
e.preventDefault(); // don't submit the form
var totalAvSum = 0; // var for the total of all subject average totals
/* for all subjects in your configuration array */
subjects.forEach(function(subject) {
/* NodeList of all inputs with Name subject.name+'Test' */
let subjResInputs = document.getElementsByName(subject.name+'Test');
let testTotal = 0;
let testCnt = 0;
let tval;
/* for each input of the subject */
Array.prototype.map.call(subjResInputs, function(t){
tval = (t.value * 1); // make sure, value is treated as number
if (tval > 0) { // only if there is a value
testTotal += tval; // add test result
testCnt += 1; // increase test count
}
});
/* calculate average and show it in output */
totalAvSum += (testTotal/testCnt);
document.getElementById(subject.name+'Average').textContent = (testTotal/testCnt);
});
/* after calculating average per subject
show total average */
document.getElementById('totalAverage').textContent = totalAvSum/subjects.length;
});
return frmTbl;
}
function createSubjectRow(s) {
var tr, td, txt, inp, outp;
/* create tr element */
tr = document.createElement('tr');
/* create td elements for subject s */
td = document.createElement('td'); // td for subject name
txt = document.createTextNode(s.name); // textNode
td.appendChild(txt); // add textNode to td
tr.appendChild(td); // add td to tr
td = document.createElement('td'); // td for subject test results
for (var i = 0; i < s.numberOfTests; i += 1) {
inp = document.createElement('input'); // create input
inp.setAttribute('type', 'number'); // set input type
// inp.setAttribute('id', s.name + 'Test' + i); // set id
/* set name attribute of input to subject name + 'Test'
all test result inputs for the same subject will have the same name */
inp.setAttribute('name', s.name + 'Test');
inp.setAttribute('step', 0.1); // in case, you give grades like 3.5
inp.setAttribute('min', 1);
inp.setAttribute('max', 100);
td.appendChild(inp); // add input to td
}
tr.appendChild(td); // add td to tr
td = document.createElement('td'); // td for average output
outp = document.createElement('output'); // create output element
outp.setAttribute('id', s.name + 'Average'); // set id
td.appendChild(outp); // add output to td
tr.appendChild(td); // add td to tr
return tr; // return the resulting table row object
}
document.getElementById('gradesForm').appendChild(createFormTable());
#formTable td {
border: solid 1px #000;
padding: 6px;
border-spacing: 3px;
}
#formTable th {
border: none;
font-size:0.9em;
text-align: left;
}
input[type="number"] {
width: 4em;
border: solid 1px #999;
margin: 0 3px;
}
<form id="gradesForm">
</form>