[编辑]修改原始问题以提高清晰度 此表单的此部分允许用户根据需要添加任意数量的行,并且可以正常工作。我的问题是我无法弄清楚如何使字符计数在克隆行的textarea上工作。 (感谢zer00ne提供了所有非常棒的帮助。非常简洁的编码!与我以前的Javascript代码相比,还提供了一小部分行的jQuery字符数。) 这是一个小提琴:https://jsfiddle.net/RationalRabbit/2vmqk26b/4/
CSS
textarea,
output,
button {font-size:inherit;}
output:nth-of-type(n+2) {margin-left:3px;}
.msg {border:none;}
.clearfix:before,
.clearfix:after {display:table; content: "";}
.clearfix:after {clear:both;}
.RowDeleteButton {float:right; font-family:arial, sansserif; font-size:14px; display:inline-block; text-decoration:none; color:#AC0F0F; font-weight:900; cursor:pointer;}
.RowDeleteButton:hover, .RowDeleteButton:focus {color:#FF0000;}
HTML
<fieldset>
<div class="parent-group">
<div class="form-group">
<input id="Name" name="Name[]" size="20" value="" />
<input type="checkbox" id="HM" name="HM[]" value="X" />
<textarea class="txt" id="TA" rows="1" cols="30" name="TA[]" maxlength="100"></textarea>
<input class='msg' name="Output" id="Output" size="3" readonly value="100" />
<input type="text" name="Location[]" id="Location" size="30" value="" />
<div class="form-group RowDelete">
<a class="RowDeleteButton" id="DeleteRow" href="javascript:void(0)"> X </a>
</div>
<div class="Clear"></div>
</div>
</div>
<div class="clearfix"></div>
<div id="container"></div>
<div class="form-group">
<a id="AddRow" href="javascript:void(0)"><span style="color:#0F61AC;">Add Row</span></a>
</div>
</fieldset>
的jQuery
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
// onkeyup invoke charCount
$('.txt').on('keyup', charCount);
// onclick...
$('#DeleteRow').closest('.form-group').hide();
$('#AddRow').on('click', function (e)
{
var len = $('.child-border').length;
$('.parent-group').clone(true, false).find(':input').each(function (idx, ele)
{
ele.name = ele.name + len;
ele.id = ele.id + len;
ele.value = '';
}).end().find('.form-group').toggle(true).end()
.toggleClass('parent-group child-border').hide()
.appendTo('#container').slideDown('slow');
});
$('#container').on('click', '[id^=DeleteRow]', function(e)
{
var jsonData = $(this).closest('.child-border, .parent-group')
.find(':input:not(button)').get()
.reduce(function (acc, ele)
{
acc[ele.name || ele.id] = ele.value;
return acc;
}, {});
$(this).closest('.child-border, .parent-group').remove();
console.log(jsonData);
});
function charCount(e)
{
// Get the text
var chars = this.value;
// Get maxlength as a number
var charMax = Number(this.getAttribute('maxlength'));
// Number of chars typed
var charDone = chars.length;
// Chars remaining is 100 - chars typed
var charToGo = charMax - charDone;
// Display chars remaining
$(this).next('.msg').val(charToGo);
}
</script>
答案 0 :(得分:2)
我已经在 Update 1 中说明了问题:
$('.parent-group').clone(true, false).find(':input')
第二个参数应为 true
这将允许clone()
方法在克隆上保留已注册的事件。注意我在第一次更新上说了同样的话,但我没有更改演示2 中的代码。
演示3 是对当前更新的OP代码的重大修改。它功能齐全,可以像演示2 一样在克隆上保留注册事件。添加的功能包括:local/sessionStorage
,将数据发送到实时测试服务器,并显示服务器响应。
演示4 是OP代码和一个简单的改变...想要猜测可能是什么?
<小时/>
克隆时,使用第一个参数确定克隆是否保留原始节点的已注册事件处理程序。
$('#original').clone(true, true);
参见演示2
<小时/> 不确定你的意思是“一排”。与OP代码相比,该演示简化了。我添加了自动高度而不是克隆行。
详情在演示
中发表
// On keyup...
$('#txt').on('keyup', function() {
// Get the text
var chars = this.value;
// if there are any linebreaks...
if (chars.match(/\n/g)) {
/* The value of rows attribute equals
|| the number of line breaks +2
*/
this.rows = chars.match(/\n/g).length + 2;
}
/* Get value of maxlength attribute convert
|| to number
*/
var charMax = Number(this.getAttribute('maxlength'));
// Number of chars typed
var charDone = chars.length;
// Chars remaining is 100 - chars typed
var charToGo = charMax - charDone;
// Display chars remaining
$('#msg').val(charToGo + ' characters remaining');
});
<textarea id='txt' rows="1" cols="30" maxlength="100"></textarea><br>
<output id='msg' for='txt'></output>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
// onkeyup invoke charCount
$('.txt').on('keyup', charCount);
// onclick...
$('button').on('click', function() {
/* clone the first .txt and .msg
|| true: keep registered events
|| false: copy content
|| set .val('') to blank
|| add to fieldset
*/
$('.txt:first').clone(true, true).val('').appendTo('fieldset');
$('.msg:first').clone(true, true).val('').appendTo('fieldset');
});
function charCount(e) {
// Get the text
var chars = this.value;
// Get maxlength as a number
var charMax = Number(this.getAttribute('maxlength'));
// Number of chars typed
var charDone = chars.length;
// Chars remaining is 100 - chars typed
var charToGo = charMax - charDone;
// Display chars remaining
$(this).next('.msg').val(charToGo + ' characters remaining');
}
textarea,
output,
button {
font-size: inherit
}
output {
display: inline-block;
vertical-align: top;
}
output:nth-of-type(n+2) {
margin-left: 3px
}
button {
margin-left: 90%
}
<button type='button'>Add</button>
<fieldset>
<textarea class='txt' rows="1" cols="30" maxlength="100"></textarea>
<output class='msg'></output>
</fieldset>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
<title></title>
<style>
textarea,
output,
button {
font-size: inherit;
}
output:nth-of-type(n+2) {
margin-left: 3px;
}
.msg {
border: none;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.del {
float: right;
margin-top: .5px;
font-family: arial, sansserif;
font-size: 14px;
display: inline-block;
text-decoration: none;
color: #AC0F0F;
font-weight: 900;
cursor: pointer;
}
.del:hover,
.del:focus {
color: #FF0000;
}
main {
display: table;
}
</style>
<script>
/* Global counter variable */
var counter = 0;
</script>
</head>
<body>
<main class="main-group">
<!--This form submits to a live test server, the [target] attribute value
is that of an iframe's name attribute. By targeting the iframe the
form can display the test server's response in the iframe
-->
<form id='main' action='https://httpbin.org/post' method='post' target='response'>
<!--The original fieldset is cloned-->
<fieldset id='set' class="form-group">
<button id="del" class='ui del' type='button'> X </button>
<input id='ID' name='ID' class='data' type='hidden'>
<input id="name" name="name" class='data name' size="25">
<input id="chx" name="chx" class='data chx' type="checkbox" value="X">
<br>
<textarea id="txt" name="txt" class='data txt' rows="1" cols="30" maxlength="100"></textarea>
<output class='ui msg'></output>
<br>
<input id="loc" name="loc" class='data loc' size="30">
</fieldset>
</form>
<nav class="btn-group">
<a id="add" href="#/" class='ui'> <b style="color:#0F61AC;">Add Row</b> </a>
<!--This submit button must use the [form] attribute with the ID of the
form to be accociated with-->
<input type='submit' form='main' class='ui'>
</nav>
<iframe src='about:blank' name='response' class='ui'></iframe>
</main>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
// Click...
$('#add').on('click', function(e) {
// Increment counter
counter++;
/* clone fieldset#set true: clone descendants /
|| TRUE: KEEP EVENTS ON CLONES
|| Gather all of the .data in clone then on each clone...
*/
var dupe = $('#set').clone(true, true);
dupe[0].id = 'set' + counter;
dupe.find('.data').each(function(idx, ele) {
// Set all .data with name and id, the counter suffix makes them unique
ele.name = this.name + counter;
ele.id = this.id + counter;
// Clear all data in each .data
ele.value = '';
// Cool animation and append clone to form#main
}).end().find('.form-group').toggle(true).end().hide().appendTo('#main').slideDown('slow');
// Clear .ui of data
dupe.find('output').val('');
dupe.find(':checkbox').prop('checked', false);
});
// Clicking any button.del...
$('.del').on('click', function(e) {
// Define arrays
var jsonData = [];
var JSONKeys = [];
// This collects all accossiated .data of ,del
var dataRow = $(this).nextAll('.data').toArray();
// This map() will create an object literal and add it to an array
jsonData = dataRow.map(function(data, idx) {
var D = {};
D.k = data.id;
D.v = data.value;
return D;
});
console.log(jsonData);
// Proceedure to timestamp data
var stamp = new Date();
var jKey = stamp.toJSON();
// Fill an array of keys for future reference
JSONKeys.push(jKey);
/* Store JSON data in sessionStorage (can be localStorage also) */
setData(jKey, jsonData);
// Save an index of the jsondata
setData('JSONKeys', jKey);
// if there's only one fieldset, reset the form if user tries to delete it
if ($('fieldset').is(':only-child')) {
$('#main')[0].reset();
} else {
// Remove fieldset
$(this).parent('.form-group').remove();
}
});
// onkeyup invoke charCount
$('.txt').on('keyup', charCount);
function charCount(e) {
// Get the text
var chars = this.value;
// Get maxlength as a number
var charMax = Number(this.getAttribute('maxlength'));
// Number of chars typed
var charDone = chars.length;
// Chars remaining is 100 - chars typed
var charToGo = charMax - charDone;
// Display chars remaining
$(this).next('.msg').val(charToGo);
}
function setData(dataKey, dataVal) {
sessionStorage.setItem(dataKey, JSON.stringify(dataVal));
}
function getData(dataKey) {
return JSON.parse(sessionStorage.getItem(dataKey));
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
<title></title>
</head>
<body>
<fieldset>
<div class="parent-group">
<div class="form-group">
<input id="Name" name="Name[]" size="20" value="" />
<input type="checkbox" id="HM" name="HM[]" value="X" />
<textarea class="txt" id="TA" rows="1" cols="30" name="TA[]" maxlength="100"></textarea>
<input class='msg' name="Output" id="Output" size="3" readonly value="100" />
<input type="text" name="Location[]" id="Location" size="30" value="" />
<div class="form-group RowDelete">
<a class="RowDeleteButton del" href="javascript:void(0)"> X </a>
</div>
<div class="Clear"></div>
</div>
</div>
<div class="clearfix"></div>
<div id="container"></div>
<div class="form-group">
<a id="AddRow" href="javascript:void(0)"><span style="color:#0F61AC;">Add Row</span></a>
</div>
</fieldset>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
// onclick...
$('#DeleteRow').closest('.form-group').hide();
$('#AddRow').on('click', function(e) {
var len = $('.child-border').length;
$('.parent-group').clone(true, true).find(':input').each(function(idx, ele) {
ele.name = ele.name + len;
ele.id = ele.id + len;
ele.value = '';
}).end().find('.form-group').toggle(true).end()
.toggleClass('parent-group child-border').hide()
.appendTo('#container').slideDown('slow');
});
$('.del').on('click', function(e) {
var jsonData = $(this).closest('.child-border, .parent-group')
.find(':input:not(button)').get()
.reduce(function(acc, ele) {
acc[ele.name || ele.id] = ele.value;
return acc;
}, {});
$(this).closest('.child-border, .parent-group').remove();
console.log(jsonData);
});
function charCount(e) {
// Get the text
var chars = this.value;
// Get maxlength as a number
var charMax = Number(this.getAttribute('maxlength'));
// Number of chars typed
var charDone = chars.length;
// Chars remaining is 100 - chars typed
var charToGo = charMax - charDone;
// Display chars remaining
$(this).next('.msg').val(charToGo);
}
// onkeyup invoke charCount
$('.txt').on('keyup', charCount);
</script>
</body>
</html>
答案 1 :(得分:1)
使用zer00ne的演示4,这是我最终提出的。差异是:
1.使用regex将第一个工作行设置为数组插入数组。也使用括号指定唯一的行输入id
2.设置删除按钮,使其不出现在第一行
3.我的脚本设置最大行数(8)
4.用于保存行数的全局变量。如果用户删除集合中间的一行或多行,则重要,然后在下面添加行
5.设置复选框,以便将值保留在克隆的行中。 &#34;检查&#34;设为false。
PHP显示了我如何处理数组服务器端,以便我按数字顺序出现数组。这可能会以不同的方式处理,但对我有用。不使用LastArrayValue变量来处理最后一个数组值,而使用ChildCount变量(例如,实际上有多少行),如果你删除,例如,行2&amp;八分之三,你只有6行,但不允许添加额外的。此外,如果没有这些变量,当在中间删除行时,您将最终得到重复的数组键。当您有最大行数要处理时,这变得更加重要 https://jsfiddle.net/uyz2zjj6/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
<title></title>
<style type="text/css">
textarea,
output,
button {font-size:inherit;}
output:nth-of-type(n+2) {margin-left:3px;}
.msg {border:none;}
.clearfix:before,
.clearfix:after {display:table; content: "";}
.clearfix:after {clear:both;}
.RowDeleteButton {float:right; font-family:arial, sansserif; font-size:14px; display:inline-block; text-decoration:none; color:#AC0F0F; font-weight:900; cursor:pointer;}
.RowDeleteButton:hover, .RowDeleteButton:focus {color:#FF0000;}
</style>
</head>
<body>
<fieldset>
<div class="parent-group">
<div class="form-group">
<input id="Name(0)" name="Name[0]" size="20" value="" />
<input type="checkbox" id="HM(0)" name="HM[0]" value="X" />
<textarea class="txt" id="TA(0)" rows="1" cols="30" name="TA[0]" maxlength="100"></textarea>
<input class='msg' name="Output" id="Output(0)" size="3" readonly value="100" />
<input type="text" name="Location[0]" id="Location(0)" size="30" value="" />
<div class="form-group" style="display:inline-block;">
<a class="RowDeleteButton del" id="DeleteRow" href="javascript:void(0)"> X </a>
</div>
<div class="Clear"></div>
</div>
</div>
<div class="clearfix"></div>
<div id="container"></div>
<div class="form-group">
<a id="AddRow" href="javascript:void(0)"><span style="color:#0F61AC;">Add Row</span></a>
</div>
</fieldset>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
// onclick...
window.LastArrayValue = 0;
$('#DeleteRow').closest('.form-group').hide();
$('#AddRow').on('click', function(e)
{
var ChildCount = $('.child-group').length;
if(ChildCount == 7)
{
alert("Sorry, 8 is the maximum number of rows");
}
else
{
var len = window.LastArrayValue;
window.LastArrayValue = len + 1;
$('.parent-group').clone(true, true).find(':input').each(function(idx, ele)
{
var ename = ele.name;
var eid = ele.id
var ArrayValue = len+1;
ele.name = ename.replace(/(\[\/?[^\]]*\])/g, "["+ArrayValue+"]");
ele.id = eid.replace(/(\(\/?[^\]]*\))/g, "("+ArrayValue+")");
if(ele.type == "checkbox"){ele.checked = false;}
else{ele.value = '';}
}).end().find('.form-group').toggle(true).end()
.toggleClass('parent-group child-group').hide()
.appendTo('#container').slideDown('slow');
}
});
$('.del').on('click', function(e)
{
var jsonData = $(this).closest('.child-group, .parent-group')
.find(':input:not(button)').get()
.reduce(function(acc, ele)
{
acc[ele.name || ele.id] = ele.value;
return acc;
}, {});
$(this).closest('.child-group, .parent-group').remove();
console.log(jsonData);
});
function charCount(e)
{
// Get the text
var chars = this.value;
// Get maxlength as a number
var charMax = Number(this.getAttribute('maxlength'));
// Number of chars typed
var charDone = chars.length;
// Chars remaining is 100 - chars typed
var charToGo = charMax - charDone;
// Display chars remaining
$(this).next('.msg').val(charToGo);
}
// onkeyup invoke charCount
$('.txt').on('keyup', charCount)
</script>
</body>
</html>
<强> PHP 强>
// Get the last key number in the array in the event that rows were deleted
// TA is the only required field. If that field is not filled, the row is ignored
end($_POST['TA']); // move the internal pointer to the end of the array
$key = key($_POST['TA']); // fetches the key of the element pointed to by the internal pointer
$Ct = $key+1; // account for array start at 0
reset($_POST['TA']); // Reset the array back to the top for processing.
$j=0; // Count only if TA has a value (revaluate $Ct when complete for later use ($Ct = $j))
for($i=0;$i<$Ct;$i++)
{
if(empty($_POST['TA'][$i])) // Empty may be that field is empty or there is no such row.
{
continue;
}
$Name[$j] = $_POST['Name'][$i];
$HM[$j] = $_POST['HM'][$i];
$TA[$j] = $_POST['TA'][$i];
$Location[$j] = $_POST['Location'][$i];
$j++;
}
$Ct = $j; // $Ct now holds the number of records and those records are in numerical order, 1-8