使用javascript添加并获得用户从动态文本框输入的值的平均值
add and get the average of values inputted by user from dynamic textboxes using javascript
我有5个文本框显示,用户可以动态添加更多的文本框。但是,我无法获得用户在这些文本框上输入的值的总和和平均值。你能帮帮我吗?我真的很感激。
下面是HTML代码:
<html>
<head>
<title> Grading System </title>
</head>
<script src="addInput.js" language="Javascript" type="text/javascript">
</script>
<body>
<center> GRADING SYSTEM <br><br>
<form method="POST">
<div id="dynamicInput">
Subject number 1<br><input type="text" name="myInputs[]"><br>
Subject number 2<br><input type="text" name="myInputs[]"><br>
Subject number 3<br><input type="text" name="myInputs[]"><br>
Subject number 4<br><input type="text" name="myInputs[]"><br>
Subject number 5<br><input type="text" name="myInputs[]">
</div>
<input type="button" value="Add a subject" onClick="addInput('dynamicInput');">
<input type="button" name="BtnCompute" onClick="avg('dynamicInput');" value="Compute Average">
</form>
</body>
</html>
Javascript代码:
var counter = 5;
var limit = 10;
var sum=0;
var average=0;
function addInput(divName){
if (counter == limit) {
alert("You have reached the limit of adding " + counter + " inputs");
}
else {
var newdiv = document.createElement('div');
newdiv.innerHTML = "Subject number " + (counter + 1) + " <br><input type='text' name='myInputs[]' >";
document.getElementById(divName).appendChild(newdiv);
counter++
}
}
function avg(divName){
sum += document.getElementsByName("myInputs[]")[0].value;
average = sum / counter
alert("Average is " + average);
return false;
}
原函数的问题:
function avg(divName) {
// you only look at one <input> element ever time, rather
// than iterating over the collection returned by
// 'document.getElementsByName("myInputs[]")'; plus you're using
// a global value which is (at least potentially) exposed to
// every other function, which makes it vulnerable to being
// over-written by other values; also: you're not ensuring that
// the entered-value is a Number (an <input> returns its value as
// a String, not a Number):
sum += document.getElementsByName("myInputs[]")[0].value;
// here you're working out the average of two global values,
// both of which could be corrupted by other functions, and
// neither of which - being global - are guaranteed to be
// Numbers, since you're not enforcing that anywhere. Also
// you've exposed yourself to potential browser interference
// by not ending the line with the (optional) semi-colon:
average = sum / counter
// this is personal, but for debugging I'd recommend use of
// console.log() (but, again, a purely personal reccomendation
// and not really a 'problem' as such):
alert("Average is " + average);
return false;
}
我对你原来的功能的更正是:
function avg() {
// rather than passing in an argument explicitly, I've opted
// to use a custom data-* attribute to contain the id of the
// relevant <div> (see the HTML below, this simplifies
// changes somewhat in the future):
var div = document.getElementById(this.dataset.divname),
// using Node.querySelectorAll() to retrieve the relevant
// <input> elements (my inclination would be to further
// amend the selector to 'input,' but that depends on
// whether or not you'd have any irrelevant <input>
// elements contained within the same <div>):
inputs = div.querySelectorAll('input[type=text][name="myInputs[]"]'),
// using Function.prototype.call() to apply the
// Array.prototoype.map() function to the Array-like
// NodeList returned by document.querySelectorAll(),
// and returns an Array of (in this case) entered values:
sum = Array.prototype.map.call(inputs, function (inputNode) {
// here we convert the existing value of the <input>
// element-node to a Number (<input> elements return
// their values as a String) or, if the value can't
// be converted to a Number, or the <input> has no
// entered value, we return 0:
return parseFloat(inputNode.value) || 0;
// using Array.prototype.reduce() to sum the
// Array of values returned by Array.prototype.map():
}).reduce(function (a, b) {
// here we return the sum of the previous number
// and the current number:
return a + b;
// the 0 here is the initial starting value
// to which the numbers are added:
}, 0),
// ensuring that the counter variable (first
// argument) is parsed into an integer value
// (using parseInt(), in base-10 (the second
// argument):
average = sum / parseInt(counter, 10);
console.log("Average is " + average);
return false;
}
// here we use JavaScript to attach the event-handling function to
// the relevant <input> (I added the 'id' to enable this), and
// this binds the avg() function to handle the 'click' event:
document.getElementById('btnCompute').addEventListener('click', avg);
修改后的<input>
元素HTML:
<input id="btnCompute" data-divname="dynamicInput" type="button" name="BtnCompute" value="Compute Average" />
var counter = 5;
var limit = 10;
function addInput() {
// note that I've amended this function also, to
// use the 'data-divname' attribute to hold the
// 'id' of the relevant <div>:
var div = document.getElementById(this.dataset.divname);
if (counter == limit) {
alert("You have reached the limit of adding " + counter + " inputs");
} else {
var newdiv = document.createElement('div');
newdiv.innerHTML = "Subject number " + (counter + 1) + " <br><input type='text' name='myInputs[]' >";
div.appendChild(newdiv);
counter++
}
}
function avg() {
var div = document.getElementById(this.dataset.divname),
inputs = div.querySelectorAll('input[type=text][name="myInputs[]"]'),
sum = Array.prototype.map.call(inputs, function(inputNode) {
return parseFloat(inputNode.value) || 0;
}).reduce(function(a, b) {
return a + b;
}, 0),
average = sum / parseInt(counter, 10);
snippet.log("Average is " + average);
return false;
}
document.getElementById('addNew').addEventListener('click', addInput);
document.getElementById('btnCompute').addEventListener('click', avg);
label,
input[type=text] {
display: block;
}
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<!--
Note that I've also amended your HTML, removing the
unnecessary <br /> elements, instead using CSS to
provide line-breaks; and also wrapping the <input>
elements in <labels>, both to enable the CSS line-
breaks and to enable clicks on the text to focus
the associated (nested) <input> element:
-->
<form method="POST" action="#">
<div id="dynamicInput">
<label>Subject number 1
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 2
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 3
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 4
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 5
<input type="text" name="myInputs[]" />
</label>
</div>
<input id="addNew" data-divname="dynamicInput" type="button" value="Add a subject" />
<input id="btnCompute" data-divname="dynamicInput" type="button" name="BtnCompute" value="Compute Average" />
外部JS demo,用于实验/开发。
尽管我纠正了你的功能,但我可能会采取另一种方法:
// because I retrieve the number of elements in a couple
// of places, I created a simple function to retrieve
// that number of elements:
function currentlyExisting(selector) {
return document.querySelectorAll(selector).length;
}
// rewritten in my own style (though this is irrelevant to the
// the question you asked):
function addNew() {
// still using the value of the custom 'data-divname'
// attribute:
var parent = document.getElementById(this.dataset.divname),
// rather than creating a HTML string, here I create nodes
// using document.createElement() and a textNode, using
// document.createTextNode():
label = document.createElement('label'),
input = document.createElement('input'),
// getting the number of currently-existing <input>
// elements using the above function, passing the
// selector as an argument:
current = currentlyExisting('input[name="myInputs[]"'),
limit = 10;
// if the current number of <input> elements is less than
// the limit:
if (current < limit) {
// we set the type of the created <input>:
input.type = 'text';
// and the name property:
input.name = 'myInputs[]';
// appending a textNode to the created <label> element:
label.appendChild(document.createTextNode('Subject number ' + (current + 1) + ':' ));
// appending the created <input> to the created <label>:
label.appendChild(input);
// attaching the created <label>, along with its own
// childNodes, to the parent div (retrieved and cached above):
parent.appendChild(label);
// setting the disabled property to true if the updated
// number of <input> elements is equal to, or greater than,
// the limit; or to false if the number of <input> elements
// remains less than the limit (preventing the addition of
// more <input> elements than that identified by the limit):
this.disabled = currentlyExisting('input[name="myInputs[]"') >= limit;
}
// all functions return a value, whether it's explicitly defined
// or undefined (as this one will), the return false of your
// original function can be added here instead if you prefer,
// but I - personally - feel it's unnecessary, so I left it out.
}
function average() {
// retrieving the relevant <div> element using the
// data-divname attribute once again:
var parent = document.getElementById(this.dataset.divname),
// retrieving the relevant <input> elements:
inputs = parent.querySelectorAll('input[name="myInputs[]"]'),
// creating an Array of the values of the relevant
// <input> elements, using Function.prototype.call()
// in order to use Array.prototype.map() on the
// Array-like NodeList returned by querySelectorAll():
values = Array.prototype.map.call(inputs, function (input) {
// returning the value of the current <input>
// element as a number to the array, using
// parseFloat() to convert that String to a
// Number; or returning 0 if the String cannot
// be parsed as a Number:
return parseFloat(input.value) || 0;
// using Array.prototype.reduce() to reduce the Array
// of numeric values (provided by map()) to a single
// number, the sum of the values:
}).reduce(function (a, b) {
// adding the previous and current values
// together:
return a + b;
// here the 0 is the initial value before the Array
// 'reduction' takes place:
}, 0),
average = sum / inputs.length;
// adding the values to the appropriate elements on screen
// for easier visualisation (and in a manner that persists):
document.getElementById('average').textContent = average;
document.getElementById('sum').textContent = sum;
document.getElementById('total').textContent = inputs.length;
}
// adding the click event handlers to the relevant button <input>
// elements using EventTarget.addEventListener():
document.getElementById('addNew').addEventListener('click', addNew);
document.getElementById('btnCompute').addEventListener('click', average);
function currentlyExisting(selector) {
return document.querySelectorAll(selector).length;
}
function addNew() {
var parent = document.getElementById(this.dataset.divname),
label = document.createElement('label'),
input = document.createElement('input'),
current = currentlyExisting('input[name="myInputs[]"'),
limit = 10;
if (current < limit) {
input.type = 'text';
input.name = 'myInputs[]';
label.appendChild(document.createTextNode('Subject number ' + (current + 1) + ':'));
label.appendChild(input);
parent.appendChild(label);
this.disabled = currentlyExisting('input[name="myInputs[]"') >= limit;
}
}
function average() {
var parent = document.getElementById('dynamicInput'),
inputs = parent.querySelectorAll('input[name="myInputs[]"]'),
sum = Array.prototype.map.call(inputs, function(input) {
return parseFloat(input.value) || 0;
}).reduce(function(a, b) {
return a + b;
}, 0),
average = sum / inputs.length;
document.getElementById('average').textContent = average;
document.getElementById('sum').textContent = sum;
document.getElementById('total').textContent = inputs.length;
}
document.getElementById('addNew').addEventListener('click', addNew);
document.getElementById('btnCompute').addEventListener('click', average);
label,
input[type=text] {
display: block;
}
#average::before {
content: 'Average: ';
}
#sum::before {
content: ', sum: ';
}
#total::before {
content: ', of: ';
}
#total::after {
content: ' entries.'
}
#total:empty::before,
#total:empty::after,
#sum:empty::before,
#average:empty::before {
content: '';
display: none;
}
<div id="results">
<span id="average"></span>
<span id="sum"></span>
<span id="total"></span>
</div>
<form method="POST" action="#">
<div id="dynamicInput">
<label>Subject number 1:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 2:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 3:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 4:
<input type="text" name="myInputs[]" />
</label>
<label>Subject number 5:
<input type="text" name="myInputs[]" />
</label>
</div>
<input id="addNew" data-divname="dynamicInput" type="button" value="Add a subject" />
<input id="btnCompute" data-divname="dynamicInput" type="button" name="BtnCompute" value="Compute Average" />
</form>
外部JS demo,用于实验/开发。
引用- HTML:
- 自定义
data-*
属性。
- 自定义
- JavaScript:
-
Array.prototype.map()
. -
Array.prototype.reduce()
. -
document.getElementById()
. -
document.getElementsByName()
. -
document.querySelectorAll()
. -
EventTarget.addEventListener()
. -
Function.prototype.call()
. -
HTMLElement.dataset
. -
parseFloat()
. -
parseInt()
.
-
所以我认为你错过了一些东西。
sum += document.getElementsByName("myInputs[]")[0].value;
这一行只获取第一个输入字段的值,因此需要循环遍历所有输入字段以获得实际的和
for (var i = 0; i < counter; i++)
sum += parseInt(document.getElementsByName("myInputs[]")[i].value);
接下来,注意parseInt();
方法的添加。这会将输入值(javascript默认将其视为字符串)转换为可以执行计算的整数。
希望对你有帮助,谢谢
相关文章:
- 让文本输入幻灯片显示输入时的新文本输入?然后向后滑动
- Sails.js:同时发布文本输入和一个文件
- 来自文本输入null的html javascript变量
- 当没有文本输入聚焦时检测空格键按下
- JS中的按钮和文本输入
- JavaScript:在调用函数的文本输入上按enter键
- jQuery自动完成功能不适用于多个文本输入
- 使用JS从选择和文本输入中捕获值,并将输出返回到HTML
- 如何通过jQuery发送选定的元素和文本输入
- HTML如何根据javascript函数的返回值限制文本输入
- jquery插件或javascript方法自动调整文本输入(而非文本区域)(固定宽度)可变高度的大小
- 在jquery中获取文本输入val始终为空
- HTML文本输入大小
- 文本输入与文本区域
- 角度ui选择标记模糊时丢失文本输入
- 文本输入值中的JavaScript变量
- Angular指令中的最佳实践是将文本输入设置为英尺和英寸的格式
- 如何在不通过模型验证的情况下屏蔽文本输入中输入的字符
- 在文本输入区域中创建双向更新
- 不使用自定义CSS或HTML(使用框架方法)的角度材质文本输入或文本区域标签大小