JavaScript:获取已编辑/更新的输入数

时间:2019-03-04 08:17:58

标签: javascript html forms input

场景

我的学生每学期至少需要学习一门科学,一门物理和一门历史考试。下面的表格给出了正确的平均成绩以及学生的最终成绩:

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

问题在于,只有在所有字段都被编辑的情况下,它才有效。如果学生不参加某些考试,平均成绩将不会显示正确的值。我知道这是因为在计算平均成绩时会除以固定数字3

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;

问题

在下面的单行中获取已更改输入字段数的简单方法是什么?我将尝试了解您的方法,然后将表单扩展为多行。

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

14 个答案:

答案 0 :(得分:17)

您似乎需要检查输入的值是否为有效数字,然后才能在计算每门课程平均值的算术中使用它们。一种方法是通过以下检查:

if (!Number.isNaN(Number.parseFloat(input.value))) {
  /* Use input.value in average calculation */
}

您还可以考虑如下所示调整脚本和HTML,这将使您能够概括并重新使用这三个类的平均值计算,如下所述:

document.getElementById('calcBtn').addEventListener('click', function() {

  /* Generalise the calculation of updates for specified course type */
  const calculateForCourse = (cls) => {

    let total = 0
    let count = 0

    /* Select inputs with supplied cls selector and iterate each element */
    for (const input of document.querySelectorAll(`input.${cls}`)) {

      if (!Number.isNaN(Number.parseFloat(input.value))) {
      
        /* If input value is non-empty, increment total and count for
        subsequent average calculation */
        total += Number.parseFloat(input.value);
        count += 1;
      }
    }

    /* Cacluate average and return result */
    return { count, average : count > 0 ? (total / count) : 0 }
  }

  /* Calculate averages using shared function for each class type */
  const calcsScience = calculateForCourse('science')
  const calcsPhysics = calculateForCourse('physics')
  const calcsHistory = calculateForCourse('history')
  
  /* Update course averages */
  document.querySelector('output.science').value = calcsScience.average
  document.querySelector('output.physics').value = calcsPhysics.average
  document.querySelector('output.history').value = calcsHistory.average
  
  /* Update course counts */
  document.querySelector('span.science').innerText = `changed:${calcsScience.count}`
  document.querySelector('span.physics').innerText = `changed:${calcsPhysics.count}`
  document.querySelector('span.history').innerText = `changed:${calcsHistory.count}`

  /* Update final grade */
  var finalGrade = document.getElementById('finalGrade');

  finalGrade.value = (calcsScience.average * 5 + calcsPhysics.average * 3 + calcsHistory.average * 2) / 10;
});
<!-- Add class to each of the course types to allow script to distinguish
     between related input and output fields -->
<form>
  Science:
  <input type="number" class="science" id="scienceTest1">
  <input type="number" class="science" id="scienceTest2">
  <input type="number" class="science" id="scienceTest3">
  <output id="scienceAverage" class="science"></output>
  <span class="science"></span>
  <br> Physics:
  <input type="number" class="physics" id="physicsTest1">
  <input type="number" class="physics" id="physicsTest2">
  <input type="number" class="physics" id="physicsTest3">
  <output id="physicsAverage" class="physics"></output>
  <span class="physics"></span>
  <br> History:
  <input type="number" class="history" id="historyTest1">
  <input type="number" class="history" id="historyTest2">
  <input type="number" class="history" id="historyTest3">
  <output id="historyAverage" class="history"></output>
  <span class="history"></span>
  <br>
  <input type="button" value="Calculate" id="calcBtn">
  <output id="finalGrade"></output>
</form>

更新

要扩展第一个答案,请参阅以下代码段中的文档以回应您的问题更新:

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');
  
  /* This variable counts the number of inputs that have changed */
  var changesDetected = 0;
  
  /* If value of test1 field "not equals" the empty string, then 
  we consider this a "changed" field, so we'll increment our 
  counter variable accordinly */
  if(test1 != '') {
    changesDetected = changesDetected + 1;
  }
  /* Apply the same increment as above for test2 field */
  if(test2 != '') {
    changesDetected = changesDetected + 1;
  }
  /* Apply the same increment as above for test3 field */
  if(test3 != '') {
    changesDetected = changesDetected + 1;
  }
  
  /* Calculate average from changesDetected counter.
  We need to account for the case where no changes
  have been detected to prevent a "divide by zero" */
  if(changesDetected != 0) {
    average.value = (Number(test1) + Number(test2) + Number(test3)) / changesDetected;
  }
  else {
    average.value = 'Cannot calculate average'
  }
  
  /* Show a dialog to box to display the number of fields changed */
  alert("Detected that " + changesDetected + " inputs have been changed")
});
<form>
  <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>

更新2

可以使用如下循环简化先前的更新:

document.getElementById('calcBtn').addEventListener('click', function() {
  
  let changesDetected = 0;
  let total = 0;
  const ids = ['test1', 'test2', 'test3'];
  
  for(const id of ids) {
    const value = document.getElementById(id).value;
    if(value != '') {
      changesDetected += 1;
      total += Number(value);
    }
  }
  
  var average = document.getElementById('average');
  
  if(changesDetected != 0) {
    average.value = total / changesDetected;
  }
  else {
    average.value = 'Cannot calculate average'
  }
    
  alert("Detected that " + changesDetected + " inputs have been changed")
});
<form>
  <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>

更新3

另一种简洁的方法based on your JSFiddle是:

document.getElementById('calculator').addEventListener('click', function() {
  var physicsAverage = document.getElementById('physicsAverage'),
    historyAverage = document.getElementById('historyAverage');

  physicsAverage.value = calculateAverageById('physics')
  historyAverage.value = calculateAverageById('history');
});

function calculateAverageById(id) {
  /* Get all input descendants of element with id */
  const inputs = document.querySelectorAll(`#${id} input`);

  /* Get all valid grade values from selected input elements */
  const grades = Array.from(inputs)
    .map(input => Number.parseFloat(input.value))
    .filter(value => !Number.isNaN(value));

  /* Return average of all grades, or fallback message if no valid grades present */
  return grades.length ? (grades.reduce((sum, grade) => (sum + grade), 0) / grades.length) : 'No assessment made!'
}
<form>
  <p id="physics">
    Physics:
    <input type="number">
    <input type="number">
    <input type="number">
    <output id="physicsAverage"></output>
  </p>
  <p id="history">
    History:
    <input type="number">
    <input type="number">
    <input type="number">
    <output id="historyAverage"></output>
  </p>
  <button type="button" id="calculator">Calculate</button>
</form>

这里的主要区别是:

  • 使用document.querySelectorAll(#$ {id}输入);和模板文字来提取具有input的元素的id元素
  • Array.from(inputs)用于将查询结果转换为数组的更易读的方式
  • 在将Number.parseFloat元素转换和过滤为有效数值以进行后续平均计算时,使用Number.isNaNinput

希望有帮助!

答案 1 :(得分:7)

一个好的开始是将您的ID更改为Class,以将您的输入放入逻辑组。下一步是从值不为null的特定组中获取输入。为此,我们可以选择例如.scienceTest,然后过滤掉空字符串项目。

我添加了一个辅助函数values,以从节点列表中提取值并将其放入普通数组中。

我们可以使用Boolean测试空字符串。我们还使用Number将所有字​​符串都转换为数字。这是在onlyNumbers函数中完成的。

接下来,我们需要计算每个组的平均值。这很容易,因为我们有一个过滤的数字列表。我们要做的就是计算总和并除以数组长度。这是通过我们小的avrg函数完成的。

 

document.getElementById('calcBtn').addEventListener('click', function() {
  var scienceTest = getGrades('.scienceTest')
  var physicsTest = getGrades('.physicsTest')
  var historyTest = getGrades('.historyTest')
  
  var scienceAverage = document.getElementById('scienceAverage');
  var physicsAverage = document.getElementById('physicsAverage');
  var historyAverage = document.getElementById('historyAverage');
  
  var finalGrade = document.getElementById('finalGrade');
  
  scienceAverage.value = avrg(scienceTest)
  physicsAverage.value = avrg(physicsTest)
  historyAverage.value = avrg(historyTest)
  
  finalGrade.value = (scienceAverage.value * 5 + physicsAverage.value * 3 + historyAverage.value * 2) / 10;
  
});

function avrg(list) {
	return list.length ? list.reduce((acc, i) => acc + i, 0) / list.length : 0
}

function getGrades(selector) {
	return onlyNumbers(values(document.querySelectorAll(selector)))
}
function onlyNumbers(list) {
		return list.filter(Boolean).map(Number)
}

function values(nodelist) {
		return Array.prototype.map.call(nodelist, (node) => node.value)
}
<form>
  Science: <input type="number" class="scienceTest">
  <input type="number" class="scienceTest">
  <input type="number" class="scienceTest">
  <output id="scienceAverage"></output>
  <br> Physics: <input type="number" class="physicsTest">
  <input type="number" class="physicsTest">
  <input type="number" class="physicsTest">
  <output id="physicsAverage"></output>
  <br> History: <input type="number" class="historyTest">
  <input type="number" class="historyTest">
  <input type="number" class="historyTest">
  <output id="historyAverage"></output>
  <br>
  <input type="button" value="Calculate" id="calcBtn">
  <output id="finalGrade"></output>
</form>

更新:简化示例

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');
  // Put all field values in array, Filter empty values out, cast values to Number
  var rowValues = [test1, test2, test3].filter(Boolean).map(Number)

  console.log('Number of changed fields', rowValues.length)

  // calculate average by reducing the array to the sum of its remaining values then divide by array length
  average.value = rowValues.reduce((sum, grade) => sum + grade, 0) / rowValues.length;
});
<form>
  <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>

更新其他内容:基于OP的jsfiddle示例中的注释

document.getElementById('calculator').addEventListener('click', function() {
  var physicsAverage = document.getElementById('physicsAverage'),
    historyAverage = document.getElementById('historyAverage');

  physicsAverage.value = calculateAverageById('physics')
  historyAverage.value = calculateAverageById('history');
});

function calculateAverageById(id) {
	// Get all inputs under Id
  var inputs = document.getElementById(id).getElementsByTagName('input')

  var values =
    Array.prototype.slice.call(inputs) // From HTMLCollection to Array
    .map(e => e.value.trim()) // Return all .value from input elements
    .filter(Boolean) // Filter out any empty strings ""
    .map(Number) // convert remaining values to Numbers
  return (values.length) ? // if length is greater then 0
    values.reduce((sum, grade) => sum + grade, 0) / values.length // Return average
    :
    'No assessment made!' // else return this message
}
    <form>
  <p id="physics">
    Physics:
    <input type="number">
    <input type="number">
    <input type="number">
    <output id="physicsAverage"></output>
  </p>
  <p id="history">
    History:
    <input type="number">
    <input type="number">
    <input type="number">
    <output id="historyAverage"></output>
  </p>
  <button type="button" id="calculator">Calculate</button>
</form>

答案 2 :(得分:5)

您不必一直将其除以3,而是可以根据学生连续更新的输入字段数来动态计算该数字。

这是工作代码:

function getValueAndTotal(element){
  var valueChanged = (element.defaultValue === element.value || element.value === "") ? 0 : 1;  
  return { value: Number(element.value), total: valueChanged };
}

document.getElementById('calcBtn').addEventListener('click', function() {
  var scienceTest1 = getValueAndTotal(document.getElementById('scienceTest1'));
  var scienceTest2 = getValueAndTotal(document.getElementById('scienceTest2'));
  var scienceTest3 = getValueAndTotal(document.getElementById('scienceTest3'));

  var physicsTest1 = getValueAndTotal(document.getElementById('physicsTest1'));
  var physicsTest2 = getValueAndTotal(document.getElementById('physicsTest2'));
  var physicsTest3 = getValueAndTotal(document.getElementById('physicsTest3'));

  var historyTest1 = getValueAndTotal(document.getElementById('historyTest1'));
  var historyTest2 = getValueAndTotal(document.getElementById('historyTest2'));
  var historyTest3 = getValueAndTotal(document.getElementById('historyTest3'));

  var scienceAverage = document.getElementById('scienceAverage');
  var physicsAverage = document.getElementById('physicsAverage');
  var historyAverage = document.getElementById('historyAverage');

  var finalGrade = document.getElementById('finalGrade');
  var scienceTotalTests = scienceTest1.total + scienceTest2.total + scienceTest3.total;
  var physicsTotalTests = physicsTest1.total + physicsTest2.total + physicsTest3.total;
  var historyTotalTests = historyTest1.total + historyTest2.total + historyTest3.total;

  scienceAverage.value = (scienceTotalTests === 0 ? 0 : (scienceTest1.value + scienceTest2.value + scienceTest3.value) / scienceTotalTests);
  physicsAverage.value = (physicsTotalTests === 0 ? 0 : (physicsTest1.value + physicsTest3.value + physicsTest3.value) / physicsTotalTests);
  historyAverage.value = (historyTotalTests === 0 ? 0 : (historyTest1.value + historyTest2.value + historyTest3.value) / historyTotalTests);

  finalGrade.value = (scienceAverage.value * 5 + physicsAverage.value * 3 + historyAverage.value * 2) / 10;
});
<form>
  Science: 
    <input type="number" id="scienceTest1" class="scienceTest">
    <input type="number" id="scienceTest2" class="scienceTest">
    <input type="number" id="scienceTest3" class="scienceTest">
    <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>

答案 3 :(得分:5)

这有点丑陋,但您可以将测试成绩视为布尔值:如果有任何测试成绩的价值为1,否则为0。

由于input.value的类型为string,因此当输入为空(false)或{{1}时,将其转换为布尔值将得到"" }(其中有数字)。

使用OP的较小代码段:

true
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 testCount = Boolean(test1) + Boolean(test2) + Boolean(test3);
  // alternatively: var testCount = !!test1 + !!test2 + !!test3

  var average = document.getElementById('average');
  average.value = (Number(test1) + Number(test2) + Number(test3)) / testCount;
});

答案 4 :(得分:4)

您需要注意代码中的两个主要瓶颈:

  1. 您正在计算每个主题的平均值,而不管它们的价值如何。从技术上讲,如果主题具有已定义的value,则仅要考虑该主题。在这种情况下,0将被计数,但空白字段将不会被计数(因为从技术上讲,学生可以在他/她的考试中获得0分)
  2. 您也正在计算加权平均值,而不管其值如何(请参见上述相同的逻辑)。

我没有尝试修改您拥有的代码,而是实际上重构了逻辑,以便根据DRY(不重复自己)原理将所有计算抽象为函数。这些功能是:

  • calculateSubjectAverage,它计算给定主题的正确平均值。它将考虑0,但忽略空字段
  • setSubjectAverage,它将设置适当的<output>元素

最后,您不必手动计算加权平均值,而可以轻松地将所有元数据存储在对象数组中,例如:

var subjects = [{
  name: 'science',
  weight: 5
}, {
  name: 'physics',
  weight: 3
}, {
  name: 'history',
  weight: 2
}];

这使我们可以过滤subjects并计算其正确的加权和,从而计算出加权平均值。需要进行过滤,因为全空主题得分有可能返回undefined

请参见下面的概念验证:

function calculateSubjectAverage(className) {
  var inputs = document.querySelectorAll('.' + className);
  var scores = Array.prototype.map.call(inputs, function(input) {
    if (input.value === '')
      return;

    return +input.value;
  });

  var count = 0;
  var scoreSum = scores.reduce(function(acc, score) {
    if (isNaN(score))
      return acc;

    count++;
    return acc + score;
  }, 0);
  
  return scoreSum / count;
};

function setSubjectAverage(className, averageScore) {
  if (isNaN(averageScore))
    return;

  document.getElementById(className + 'Average').value = averageScore;
}

document.getElementById('calcBtn').addEventListener('click', function() {
  var subjects = [{
    name: 'science',
    weight: 5
  }, {
    name: 'physics',
    weight: 3
  }, {
    name: 'history',
    weight: 2
  }];
  
  var totalWeight = 0;

  // Go through each subject and calculate & set average score
  // Since we are iterating anyway, might want to calculate totalWeight, too
  subjects.forEach(function(subject) {
    var averageScore = calculateSubjectAverage(subject.name);
    setSubjectAverage(subject.name, averageScore);
    
    // Set average score to object
    subject.average = averageScore;
    
    if (!isNaN(averageScore))
      totalWeight += subject.weight;
  });
  
  // Only compute weighted average from subject with valid averages
  var weightedTotal = subjects.reduce(function(acc, subject) {
    if (isNaN(subject.average))
      return acc;
      
    return acc + subject.average * subject.weight;
  }, 0);
  var weightedAverage = weightedTotal / totalWeight;
  if (!isNaN(weightedTotal / totalWeight))
    document.getElementById('finalGrade').value = weightedTotal / totalWeight;
});
<form>
  Science: <input type="number" class="science">
  <input type="number" class="science">
  <input type="number" class="science">
  <output id="scienceAverage"></output>
  <br> Physics: <input type="number" class="physics">
  <input type="number" class="physics">
  <input type="number" class="physics">
  <output id="physicsAverage"></output>
  <br> History: <input type="number" class="history">
  <input type="number" class="history">
  <input type="number" class="history">
  <output id="historyAverage"></output>
  <br>
  <input type="button" value="Calculate" id="calcBtn">
  <output id="finalGrade"></output>
</form>

答案 5 :(得分:4)

var tests = [
    document.getElementById('test1').value || false,
    document.getElementById('test2').value || false,
    document.getElementById('test3').value || false
];

var average = 0,
    length = 0;

for (var i = 0; i < tests.length; i++) {
    if (tests[i] !== false) {
        average += Number( tests[i] );
        length ++;
    }
}

average = average / length;

这是ES5解决方案。您可以做的短一些,但是我认为这很直观。

答案 6 :(得分:3)

您正在将值除以3,这就是为什么结果比预期少的原因。

  • 动态制作代码的html。
  • 不要做太多变量(scienceTest1,scienceTest2.....),而使用循环将值存储在array
  • 像这样的Number(scienceTest1) + Number(scienceTest2) + Number(scienceTest3)) / 3写作是不好的,因为您可以进行更多的测试,并且有更多的类型错误机会。而是将值存储在数组中,最后使用Array.prototype.reduce()添加它们。
  • 对于values数组,您需要在将value !==''推入数组之前检查其是否//This is list of subjects. You can change it will work same let subjects = ['science','physics','history']; let noOfTests = 3; //add <form> element to body document.body.innerHTML = '<form></form>' //getting that form as an element. let form = document.querySelector('form') //Creating the HTML dymamically subjects.forEach(sub =>{ //setting the title of the subject form.innerHTML += sub + ':' + '<br>'; for(let i = 0;i<noOfTests;i++){ //generating input feilds equal of 'noOfTests' for each subject form.innerHTML += `<input type="number" id="${sub}Test${i+1}" /><br>` } //adding the output element to after addign all inputs. form.innerHTML += `<output id="${sub}Average"></output><br>` }) //Adding calculate button and finalOuput element. form.innerHTML += `<br><input type="button" value="Calculate" id="calcBtn"> <output id="finalGrade"></output>` document.getElementById('calcBtn').addEventListener('click', function() { //'total' is array which will contain average of each subject let total = []; //looping thorugh each subject in 'subjects' array. subjects.forEach(sub => { //'vals' will store the values currect subject we are looping let vals = [] for(let i = 0;i<noOfTests;i++){ //getting the value of each input feild of current subject let val = document.getElementById(`${sub}Test${i+1}`).value; //check if input have a value so we push it into the vals array. if(val !== '') vals.push(val); } //getting average of all values using reduce let result = vals.reduce((ac,a) => ac + Number(a),0)/vals.length; //adding result(average) to the output of current subject. document.getElementById(`${sub}Average`).innerHTML = result //adding the average of current subject of the 'total' array. total.push(result); }) //At last find the average of total averages and add it to 'finalGrade' total = total.filter(x => !isNaN(x)); document.getElementById('finalGrade').innerHTML = total.reduce((ac,a) => ac + a,0)/total.length; });,以便获得正确的平均值。

代码是完全动态的,您可以具有任何主题以及任何  没有测试

input{
  border-radius:5px;
  padding:3px;
  margin:5px;
  font-size:20px;
}
form{
  font-size:20px;
  font-family:sans-serif;
  text-transform:capitalize;
}
bulkCopy.DestinationTableName = "dbo.Student";
bulkCopy.ColumnMappings.Add("<list field name>", "<database field name>");
//Map all your column as above
bulkCopy.WriteToServer(dataTable);

答案 7 :(得分:3)

在该功能中引入计数器。每次单击后,检查输入值是否为”,因为输入值将在转换前为字符串。如果不执行任何操作,则三元操作将返回 false

 test1!=''?num++:false;

如果test1=='',则 false ,否则增加计数器。在计算平均值之前,请检查计数器是否为0,是否将其设置为1。如果为零,则除以零的结果将为无穷大,我们将获得 NaN 作为输出,并将计数器设置为1将得到0。

document.getElementById('calcBtn').addEventListener('click', function() {
let num=0;
  var test1 = document.getElementById('test1').value;
  test1!=''?num++:false;
  var test2 = document.getElementById('test2').value;
  test2!=''?num++:false;
  var test3 = document.getElementById('test3').value;
  test3!=''?num++:false;
  var average = document.getElementById('average');
  num==0?num++:false;
  average.value = (Number(test1) + Number(test2) + Number(test3)) / num;
});
<form>
  <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>

答案 8 :(得分:2)

已经有一些解决方案。这是我的。

您的代码中有一些需要优化的地方,我认为基于像这样的简单配置数组动态创建HTML是一个好主意

const subjects = [{
  name: 'science',
  numberOfTests: 3
}, {
  name: 'physics',
  numberOfTests: 2
}, {
  name: 'history',
  numberOfTests: 3
}];

因此,如果主题或测试数量发生变化,则无需更改代码,而只需更改此配置。 我试图注释我的代码示例以使其易于理解,在此做什么。但这不是重要的部分。更重要的是 您需要了解计算部分中发生的事情以确保计算正确。所以我从那部分开始。

如果在测试结果输入中添加 name 属性,并为相同主题的每个输入赋予相同的名称,则可以轻松地检索该输入的NodeList,检查每个Node的值并计算基于它。这样您就知道了学生为每门科目所进行的测试的值和次数。

让我们看看

/* 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>

答案 9 :(得分:2)

  

您可以创建一个名为tests = {}的对象,并将测试作为数组保留在其中,以举一个例子为例,我仅针对一个主题编写了一些代码,您可以在下面对其进行修改。

此外,我使用了 classes 而不是 id的来减少代码的复杂性和重复性。

更新:更新了以下代码,以使动态和灵活适应多个主题。

document.getElementById('calcBtn').addEventListener('click', function() {

  // create a tests object and have subject specific test inside it
  var tests = {
    "Science": [],
    "Physics": [],
    "History": []
  };

  //looping over all subjects 
  for (var key in tests) {

    // this can be looped as well if you have multiple subjects
    var test = document.getElementsByClassName(key);
    for (i = 0; i < test.length; i++) {
      if (test[i].value != null || test[i].value != "") {
        tests[key].push(Number(test[i].value));
      }
    }

    // populate average by using reduce function 
    document.getElementById(key).value = tests[key].reduce((prev, curr) => prev + curr) / tests[key].length;
  }
});
<form>
  <fieldset>
    <legend>Science</legend>
    <input type="number" class="Science">
    <input type="number" class="Science">
    <input type="number" class="Science">
    <output id="Science"></output>
  </fieldset>

  <fieldset>
    <legend>Physics</legend>
    <input type="number" class="Physics">
    <input type="number" class="Physics">
    <input type="number" class="Physics">
    <output id="Physics"></output>
  </fieldset>

  <fieldset>
    <legend>History</legend>
    <input type="number" class="History">
    <input type="number" class="History">
    <input type="number" class="History">
    <output id="History"></output>
  </fieldset>
  <br>
  <input type="button" value="Calculate" id="calcBtn">
</form>

答案 10 :(得分:2)

方法

使用Array length属性并使此值动态化,可以解决固定数字3的除法问题。

第1步:将所有主题放入数组中,并过滤输入内容是否有值。

步骤2 :按主题获取平均值。

第3步:使用权重计算最终得分。

有两个功能被多次使用。 isTruthyaverage

为简化平均功能,我将其分解为sumaverage

document.getElementById('calcBtn').addEventListener('click', function() {

  // Helper Functions

  function isTruthy (score) {
    return !!score
  }
  
  function sum (scores) {
    var total = 0;
    for (var counter=0; counter<scores.length; counter++) {
      total += (Number(scores[counter]) || 0);
    }
    return total
  }
  
  function average (scores) {
    return (sum(scores) / scores.length) || 0
  }
  
  // Step 1
  var scienceScores = [
    document.getElementById('scienceTest1').value,
    document.getElementById('scienceTest2').value,
    document.getElementById('scienceTest3').value
  ].filter(isTruthy)
  
  var physicsScores = [
    document.getElementById('physicsTest1').value,
    document.getElementById('physicsTest2').value,
    document.getElementById('physicsTest3').value
  ].filter(isTruthy)
  
  var historyScores = [
    document.getElementById('historyTest1').value,
    document.getElementById('historyTest2').value,
    document.getElementById('historyTest3').value
  ].filter(isTruthy)
  
  var scienceAverage = document.getElementById('scienceAverage');
  var physicsAverage = document.getElementById('physicsAverage');
  var historyAverage = document.getElementById('historyAverage');
  
  var finalGrade = document.getElementById('finalGrade');
  
  // Step 2
  scienceAverage.value = average(scienceScores);
  physicsAverage.value = average(physicsScores);
  historyAverage.value = average(historyScores);
  
  // Step 3
  finalGrade.value = (scienceAverage.value * 5 + physicsAverage.value * 3 + historyAverage.value * 2) / 10;
});
<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>

答案 11 :(得分:2)

  

什么是获取更改的输入字段数的简单方法

我们可以为每个input分配一个自定义data属性,该属性等于input的值,然后在对空值进行计数时将其排除。以下通用脚本可用于任意数量的课程:

var form = document.querySelector('form');

function calculateAverage(fieldset) {
  var total = 0;
  var inputs = fieldset.querySelectorAll('input');
  for (var input of inputs) {
    total += Number(input.value);
    input.dataset.value = input.value;
  }
  return total / fieldset.querySelectorAll('input:not([data-value=""])').length;
}

function displayAverages() {
  var fieldsets = form.querySelectorAll('fieldset');
  for (var fieldset of fieldsets) {
    var avg = calculateAverage(fieldset);
    var output = fieldset.querySelector('output');
    if (isNaN(avg)) {
      output.value = 'Please enter a grade.';
    } else {
      output.value = 'Average: ' + avg.toFixed(1);
    }
  }
}

form.querySelector('button').addEventListener('click', displayAverages);
body {
  display: flex;
}

fieldset {
  margin: 0 0 16px;
}

input {
  width: 4em;
}

output {
  display: block;
  height: 1em;
  margin: 8px 0 0 2px;
}
<form>
  <fieldset>
    <legend>Physics</legend>
    <input type="number">
    <input type="number">
    <input type="number">
    <output></output>
  </fieldset>
  <fieldset>
    <legend>History</legend>
    <input type="number">
    <input type="number">
    <input type="number">
    <output></output>
  </fieldset>
  <button type="button">Calculate</button>
</form>

答案 12 :(得分:1)

document.getElementById('calcBtn').addEventListener('click', function() {
var testcount = [];
var count = 0;
testcount = Array.prototype.slice.call(document.getElementsByClassName('test1'))
for(var i=0;i<testcount.length;i++)
{
 if(Number(testcount[i].value) > 0)
 {
  count=count+1;
  }
 
}
  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)) / count;
});
<form>
  <input type="number" class="test1" id="test1">
  <input type="number" class="test1" id="test2">
  <input type="number" class="test1" id="test3">
  <output id="average"></output>
  <br>
  <input type="button" value="Calculate" id="calcBtn">
</form>

在上面的

中,通过在输入标签中添加class属性来实现输出(给同一组控件使用相同的名称)。 第二件事是遍历该类对象以获取非空或非零值的计数。

答案 13 :(得分:1)

我的建议是,避免多个输入字段,以便读取多个输入。就我而言,我使用分号来分隔输入字段内的各个值。这样,我可以输入所需的任意多个值(至少一个值)。因此,我的表单如下所示:

<!-- form.html -->
<form>
  Science: <input type="text" id="scienceTest">
  <output id="scienceAverage"></output>
  <br> Physics: <input type="text" id="physicsTest">
  <output id="physicsAverage"></output>
  <br> History: <input type="text" id="historyTest">
  <output id="historyAverage"></output>
  <br>
  <input type="button" value="Calculate" id="calcBtn">
  <output id="finalGrade"></output>
</form>
<script src="script.js"></script>

我的JavaScript看起来像这样:

// script.js
(function() {
  var scienceTest = document.getElementById('scienceTest');
  var physicsTest = document.getElementById('physicsTest');
  var historyTest = document.getElementById('historyTest');
  var scienceAverage = document.getElementById('scienceAverage');
  var physicsAverage = document.getElementById('physicsAverage');
  var historyAverage = document.getElementById('historyAverage');
  var finalGrade = document.getElementById('finalGrade');

  function sumArray(sum, item) {
    return sum + item;
  }

  document.getElementById('calcBtn').addEventListener('click', function() {
    // fetch the string of the input and split into its separate numbers
    var scienceGradeStrings = scienceTest.value.split(";");
    var physicsGradeStrings = physicsTest.value.split(";");
    var historyGradeStrings = historyTest.value.split(";");

    // calculate the averages
    scienceAverage.value = scienceGradeStrings
      // convert the grades from strings to numbers
      .map(Number)
      // sum all grades together
      .reduce(sumArray, 0)
      // calculate the average grade
      / scienceGradeStrings.length;
    physicsAverage.value = physicsGradeStrings.map(Number).reduce(sumArray, 0) / physicsGradeStrings.length;
    historyAverage.value = historyGradeStrings.map(Number).reduce(sumArray, 0) / historyGradeStrings.length;
    finalGrade.value = (scienceAverage.value * 5 + physicsAverage.value * 3 + historyAverage.value * 2) / 10;
  });
})();