我一直在玩javascript / jquery,试图让各种各样的脚本很好地一起玩(例如参见我最近在Populate an input with the contents of a custom option (data-) attribute提出的问题)。
我现在得到了大量有用的代码。它应该做4件事:
第一行一切正常。但是,对于克隆的行,步骤1和4停止工作。我怀疑它是因为我没有唯一地识别每个克隆的实例。是对的吗?那我该怎么做呢?
function cloneRow() {
var row = document.getElementById("myrow"); // find row to copy
var table = document.getElementById("mytable"); // find table to append to
var clone = row.cloneNode(true); // copy children too
clone.id = "newID"; // change id or other attributes/contents
table.appendChild(clone); // add new row to end of table
}
function createRow() {
var row = document.createElement('tr'); // create row node
var col = document.createElement('td'); // create column node
var col2 = document.createElement('td'); // create second column node
row.appendChild(col); // append first column to row
row.appendChild(col2); // append second column to row
col.innerHTML = "qwe"; // put data in first column
col2.innerHTML = "rty"; // put data in second column
var table = document.getElementById("tableToModify"); // find table to append to
table.appendChild(row); // append row to table
}
window.sumInputs = function() {
var inputs = document.getElementsByName('hours'),
result = document.getElementById('total'),
sum = 0;
for(var i=0; i<inputs.length; i++) {
var ip = inputs[i];
if (ip.name && ip.name.indexOf("total") < 0) {
sum += parseFloat(ip.value) || 0;
}
}
result.value = sum;
}
var myJson =
{
"listItems":[
{
"id":"1",
"project_no":"1001",
"task":[
{
"task_description":"Folding stuff",
"id":"111",
"task_summary":"Folding",
},
{
"task_description":"Drawing stuff",
"id":"222",
"task_summary":"Drawing"
}
]
},
{
"id":"2",
"project_no":"1002",
"task":[
{
"task_description":"Meeting description",
"id":"333",
"task_summary":"Meeting"
},
{
"task_description":"Administration",
"id":"444",
"task_summary":"Admin"
}
]
}
]
}
$(function(){
$.each(myJson.listItems, function (index, value) {
$("#project").append('<option value="'+value.id+'">'+value.project_no+'</option>');
});
$('#project').on('change', function(){
$('#task').html('<option value="000">-Select Task-</option>');
for(var i = 0; i < myJson.listItems.length; i++)
{
if(myJson.listItems[i].id == $(this).val())
{
$.each(myJson.listItems[i].task, function (index, value) {
$("#task").append('<option value="'+value.id+'" data-description="'+value.task_description+'">'+value.task_summary+'</option>');
});
}
}
});
});
$('#task').change(function() {
$('#taskText').val( $(this).find('option:selected').data('description') )
})
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<html>
<form>
<table id='mytable'>
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input size=3 id='total' disabled='disabled'/></td>
<td></td>
</tr>
<tr id='myrow'>
<td>
<select id="project" name="">
<option value="">Select One</option>
</select>
</td><td>
<select id="task" name="" onchange="updateText('task')">>
<option value="">Select One</option>
</select>
</td>
<td>
<select name = 'hours' onmouseup="sumInputs()">
<option>1.0</option>
<option>1.5</option>
<option>2.0</option>
</select>
<td><input type="text" value="" id="taskText" /></td>
</tr>
</table>
<input type="button" onclick="cloneRow()" value="Add Row" />
</form>
&#13;
如果我错误地插入了代码段,请致歉。
答案 0 :(得分:11)
我要走出困境并提出一些完全不同的建议。我知道你的问题是用jQuery
标记的,但我想建议一个不同的,我相信更好的方法来解决问题。
我认为你在这里混合了你的 DOM 和你的 JavaScript ,而使用绑定框架可能更简单。这些绑定框架将您的演示文稿从基础数据中分离出来。有一些可供选择,这里只是几个:
我个人非常了解Knockout,所以这就是我如何创建类似于你使用Knockout制作的东西。在这里,我将代码减少了大约50%,我相信我已经从JavaScript中删除了所有 DOM 的奖励,显着提高了可读性
请注意,通过使用 Knockout.mapping 插件并调用ko.toJS(vm.jobs)
<,您可以轻松获取JSON视图(我的结构可能与您的结构不完全匹配,但应该会提供您的想法) / p>
var vm = {};
vm.projects = ko.observableArray([]);
vm.projects.push({
id: 1001,
name: "Project A",
stages: [{ name: "folding", description: "folding stuff" },
{ name: "drawing", description: "drawing shapes" }]
});
vm.projects.push({
id: 1002,
name: "Project B",
stages: [{ name: "meeting", description: "Talking" },
{ name: "admin", description: "everyday things" }]
});
vm.jobs = ko.observableArray([]);
vm.totalHours = ko.computed(function() {
var sum = 0;
for (var i = 0; i < vm.jobs().length; i++) {
sum += vm.jobs()[i].time();
}
return sum;
})
createJob = function() {
var job = {};
// Set fields on the job
job.project = ko.observable();
job.stage = ko.observable();
job.stages = ko.computed(function() {
if (job.project()) return job.project().stages;
return [];
});
job.stage.subscribe(function() {
job.description(job.stage().description);
});
job.description = ko.observable();
job.time = ko.observable(1);
vm.jobs.push(job);
};
createJob();
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<form>
<table id='mytable'>
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<input size=3 id='total' disabled='disabled' data-bind="value: totalHours" />
</td>
<td></td>
</tr>
<!-- ko foreach: jobs -->
<tr id='myrow'>
<td>
<select class="project" data-bind="options: $root.projects, optionsText: 'name', value: project, optionsCaption: 'Select a Project'"></select>
</td>
<td>
<select class="stage" data-bind="options: stages, value: stage, optionsText: 'name', optionsCaption: 'Select a Stage', enable: project()"></select>
</td>
<td>
<select class="hours" data-bind="options: [1.0, 1.5, 2.0], value: time, enable: stage()"></select>
</td>
<td>
<input type="text" data-bind="value: description, enable: stage()" />
</td>
</tr>
<!-- /ko -->
</table>
<input type="button" onclick="createJob()" value="Add Row" />
</form>
这里的一般想法是observable
和observableArray
都使用Knockout
绑定到 DOM 。它们会自动保持彼此同步。
computed
字段本质上是一个计算字段,所以我用它来生成你的总数,也用它来提供其中一个下拉菜单 - 你可能会使用不同的方法,但这看起来很简单。 / p>
最后有一个手册subscribe
,旨在更新您手头的job
的默认说明。这允许您更新字段,而不必使用其他可观察或计算字段来覆盖描述,如果用户设置了一个。
由于您的身份证也存在问题,我还要提一下如何解决这些问题。使用Knockout,我们可以很容易地使用另一个计算字段为我们的 DOM 创建客户端唯一ID,并在数组中返回它的顺序:
job.id = ko.computed(function() {
return vm.jobs.indexOf(job);
});
您甚至可以在 DOM 中反映出这一点(请注意ID不能以数字开头),如下所示:
<td data-bind="attr: { id: 'job_' + id() }"></td>
这会生成DOM,如:
<td id="job_0"></td>
答案 1 :(得分:3)
这是一个非常肮脏的代码和平。我用同样的脏样式解决了你的问题,但我建议你以适当的方式写出全新的东西。 我通过将所有id更改为类属性来解决问题。每行都有自己唯一的ID。
var rowNum =1;
function cloneRow() {
var row = document.getElementById("row0"); // find row to copy
var table = document.getElementById("mytable"); // find table to append to
var clone = row.cloneNode(true); // copy children too
clone.id = "row"+rowNum; // change id or other attributes/contents
table.appendChild(clone); // add new row to end of table
initProject(clone.id);
rowNum++;
}
function createRow() {
var row = document.createElement('tr'); // create row node
var col = document.createElement('td'); // create column node
var col2 = document.createElement('td'); // create second column node
row.appendChild(col); // append first column to row
row.appendChild(col2); // append second column to row
col.innerHTML = "qwe"; // put data in first column
col2.innerHTML = "rty"; // put data in second column
var table = document.getElementById("tableToModify"); // find table to append to
table.appendChild(row); // append row to table
}
window.sumInputs = function() {
var inputs = document.getElementsByName('hours'),
result = document.getElementById('total'),
sum = 0;
for(var i=0; i<inputs.length; i++) {
var ip = inputs[i];
if (ip.name && ip.name.indexOf("total") < 0) {
sum += parseFloat(ip.value) || 0;
}
}
result.value = sum;
}
var myJson =
{
"listItems":[
{
"id":"1",
"project_no":"1001",
"task":[
{
"task_description":"Folding stuff",
"id":"111",
"task_summary":"Folding",
},
{
"task_description":"Drawing stuff",
"id":"222",
"task_summary":"Drawing"
}
]
},
{
"id":"2",
"project_no":"1002",
"task":[
{
"task_description":"Meeting description",
"id":"333",
"task_summary":"Meeting"
},
{
"task_description":"Administration",
"id":"444",
"task_summary":"Admin"
}
]
}
]
}
function initProject(rowId){
console.log(rowId);
if(rowId == 'row0'){
$.each(myJson.listItems, function (index, value) {
$("#"+rowId+" .project").append('<option value="'+value.id+'">'+value.project_no+'</option>');
});
}
$('#'+rowId+' .project').on('change', function(e){
rowElem = e.target.closest(".row");
$('#'+rowId+' .task').html('<option value="000">-Select Task-</option>');
for(var i = 0; i < myJson.listItems.length; i++)
{
if(myJson.listItems[i].id == $(this).val())
{
$.each(myJson.listItems[i].task, function (index, value) {
$('#'+rowId+' .task').append('<option value="'+value.id+'" data-description="'+value.task_description+'">'+value.task_summary+'</option>');
});
}
}
});
$('#'+rowId+' .task').change(function() {
$('#'+rowId+' .taskText').val( $(this).find('option:selected').data('description') )
})
}
initProject('row0');
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<html>
<head>
</head>
<body>
<form>
<table id='mytable'>
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input size=3 id='total' disabled='disabled'/></td>
<td></td>
</tr>
<tr id='row0' class="row">
<td>
<select class="project" name="">
<option value="">Select One</option>
</select>
</td><td>
<select class="task" name="" onchange="updateText('task')">
<option value="">Select One</option>
</select>
</td>
<td>
<select name = 'hours' onmouseup="sumInputs()">
<option>1.0</option>
<option>1.5</option>
<option>2.0</option>
</select>
<td><input type="text" value="" class="taskText" /></td>
</tr>
</table>
<input type="button" onclick="cloneRow()" value="Add Row" />
</form>
</body>
&#13;
答案 2 :(得分:2)
另一个带有简单模板系统的版本,这次没有jquery。
有趣的是看它是否难以转换,但事实并非如此,将jQuery代码映射到现代浏览器的普通JS的大部分内容都可以在这里找到 - YOU MIGHT NOT NEED JQUERY
以下是代码:
var myJson =
{
"listItems":[
{
"id":"1",
"project_no":"1001",
"task":[
{
"task_description":"Folding stuff",
"id":"111",
"task_summary":"Folding",
},
{
"task_description":"Drawing stuff",
"id":"222",
"task_summary":"Drawing"
}
]
},
{
"id":"2",
"project_no":"1002",
"task":[
{
"task_description":"Meeting description",
"id":"333",
"task_summary":"Meeting"
},
{
"task_description":"Administration",
"id":"444",
"task_summary":"Admin"
}
]
}
]
}
var template = function(target) {
var _parent = target.parentNode;
var _template = _parent.getAttribute('dataTemplate');
if (!_template) {
// no template yet - save it and remove the node from HTML
target.style.display = '';
target.classList.add('clone');
_template = target.outerHTML;
_parent.setAttribute('dataTemplate', JSON.stringify(_template));
_parent.removeChild(target);
} else {
// use saved template
_template = JSON.parse(_template);
}
return {
populate: function(data) {
var self = this;
this.clear();
data.forEach(function(value) {
self.clone(value);
});
},
clone: function(value) {
var clone = target.cloneNode(true);
_parent.appendChild(clone);
var html = _template;
if (value) {
for (var key in value) {
html = html.replace('{'+key+'}', value[key]);
}
}
clone.outerHTML = html;
clone = _parent.lastChild;
if (value) {
clone.setAttribute('dataTemplateData', JSON.stringify(value));
}
return clone;
},
clear: function() {
var clones = _parent.querySelectorAll('.clone')
Array.prototype.forEach.call(clones, function(el) {
_parent.removeChild(el);
});
}
};
};
function createRow() {
var clone = template(document.querySelector('.myrow-template')).clone();
template(clone.querySelector('.project-option-tpl')).populate(myJson.listItems);
updateHours();
bindEvents();
}
function bindEvents() {
var elements = document.querySelectorAll('#mytable .project');
Array.prototype.forEach.call(elements, function(elem) {
elem.addEventListener('change', function() {
var data = JSON.parse(this.options[this.selectedIndex].getAttribute('dataTemplateData'));
template(this.parentNode.parentNode.querySelector('.task-option-tpl')).populate(data.task);
});
});
elements = document.querySelectorAll('#mytable .task');
Array.prototype.forEach.call(elements, function(elem) {
elem.addEventListener('change', function() {
var data = JSON.parse(this.options[this.selectedIndex].getAttribute('dataTemplateData'));
this.parentNode.parentNode.querySelector('.task-text').value = data.task_description;
});
});
elements = document.querySelectorAll('#mytable .hours');
Array.prototype.forEach.call(elements, function(elem) {
elem.addEventListener('mouseup', function() {
updateHours();
});
});
}
function updateHours() {
var total = 0;
var hours = document.querySelectorAll('.hours');
Array.prototype.forEach.call(hours, function(item) {
if (item.parentNode.parentNode.style.display.length == 0) {
total += parseFloat(item.value) || 0;
}
});
document.getElementById('total').value = total;
}
function ready(fn) {
if (document.readyState != 'loading'){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function(){
createRow();
document.querySelector('.add-row').addEventListener('click', function() {
createRow();
});
});
<form>
<table id='mytable'>
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input size=3 id='total' disabled='disabled'/></td>
<td></td>
</tr>
<tr class='myrow-template' style='display:none'>
<td>
<select class="project" name="">
<option value="">Select One</option>
<option class='project-option-tpl' value="{id}" style='display:none'>{project_no}</option>
</select>
</td>
<td>
<select class="task" name="">
<option value="000">-Select Task-</option>
<option class='task-option-tpl' value="{id}" data-description="{task_description}" style='display:none'>{task_summary}</option>
</select>
</td>
<td>
<select class='hours'>
<option>1.0</option>
<option>1.5</option>
<option>2.0</option>
</select>
</td>
<td><input type="text" value="" class="task-text" /></td>
</tr>
</table>
<input type="button" class="add-row" value="Add Row" />
</form>
答案 3 :(得分:1)
请参阅其他答案为什么当前代码未按预期工作。 (简而言之:id
可能只出现一次)。
我花了一些时间重组你的代码。就个人而言,它看起来并不可维护。我确实使用过物体来吸引东西。你也可以使用像angular等框架。但是自己的JavaScript也可以非常好。对于那些小的东西来说可能已经足够了。
一些注意事项:
display:none
并且称为模板(这样我们总是可以使用“干净”模板)。您只能在Javascript中使用此模板并按需插入,但这种方式可以帮助您快速编辑设计。select
都会执行相同的功能。但由于我们每次使用不同的数据时都会使用对象。hours.click(function(){tableElement.updateTime()});
这个内部函数是必需的,因为它将函数的范围保持为对象而不是click事件。通常,当您捕获单击事件并设置函数时,在此函数中this
是单击事件。我使用了对象,因此this
应该是对象,而不是事件。
var myJson = {
"listItems": [{
"id": "1",
"project_no": "1001",
"task": [{
"task_description": "Folding stuff",
"id": "111",
"task_summary": "Folding",
}, {
"task_description": "Drawing stuff",
"id": "222",
"task_summary": "Drawing"
}]
}, {
"id": "2",
"project_no": "1002",
"task": [{
"task_description": "Meeting description",
"id": "333",
"task_summary": "Meeting"
}, {
"task_description": "Administration",
"id": "444",
"task_summary": "Admin"
}]
}]
};
/*
Table
*/
function projectPlan(tableElement) { // Constructor
this.tableElement = tableElement;
this.totalTimeElement = tableElement.find('tr td input.total');
this.rows = [];
};
projectPlan.prototype.appendRow = function(template) { // you could provide different templates
var newRow = template.clone().toggle(); // make a copy and make it visible
this.tableElement.append(newRow);
this.rows.push( new workRow(newRow, this) );
// update the time right away
this.updateTime();
};
projectPlan.prototype.updateTime = function() {
var totalWork = 0;
for(var i = 0; i < this.rows.length; i++) totalWork += this.rows[i].hours.val()*1; // *1 makes it a number, default is string
this.totalTimeElement.val(totalWork);
};
/*
Row
*/
function workRow(rowElement, tableElement) { // Constructor
// set the object attributes with "this"
this.rowElement = rowElement;
this.tableElement = tableElement;
this.projects = rowElement.find( "td select.projects" );
this.tasks = rowElement.find( "td select.tasks" );
this.hours = rowElement.find( "td select.hours" );
this.taskText = rowElement.find( "td input.taskText" );
// set all the event listeners, don't use this since the "function(){" will have a new scope
var self = this;
this.projects.change(function(){self.updateTasks()});
this.tasks.change(function(){self.updateTaskText()});
this.hours.change(function(){tableElement.updateTime()});
}
workRow.prototype.updateTasks = function() {
// delete the old ones // not the first because it's the title
this.tasks.find('option:not(:first-child)').remove();
if(this.projects.val() != "-1") {
var tmpData;
for (var i = 0; i < myJson.listItems[this.projects.val()].task.length; i++) {
tmpData = myJson.listItems[this.projects.val()].task[i];
this.tasks.append('<option value="' + i + '">' + tmpData.task_summary + '</option>');
}
}
this.taskText.val('');
}
workRow.prototype.updateTaskText = function() {
if(this.tasks.val() == "-1") this.taskText.val('');
else this.taskText.val( myJson.listItems[ this.projects.val() ].task[ this.tasks.val() ].task_description );
}
/*
Setup
*/
// Prepare the template (insert as much as possible now)
rowTemplate = $('#rowTemplate');
var projectList = rowTemplate.find( "td select.projects" );
var tmpData;
for (var i = 0; i < myJson.listItems.length; i++) {
tmpData = myJson.listItems[i];
projectList.append('<option value="' + i + '">' + tmpData.project_no + '</option>');
}
// setup table
var projectPlan = new projectPlan( $('#projectPlan') );
// Print the first row
projectPlan.appendRow(rowTemplate);
$('#buttonAddRow').click(function(){ projectPlan.appendRow(rowTemplate) });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<table id="projectPlan">
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input size=3 class='total' disabled='disabled' /></td>
<td></td>
</tr>
<tr id='rowTemplate' style="display:none">
<td>
<select class="projects">
<option value="-1">Select One</option>
</select>
</td>
<td>
<select class="tasks">>
<option value="-1">Select One</option>
</select>
</td>
<td>
<select class='hours'>
<option>1.0</option>
<option>1.5</option>
<option>2.0</option>
</select>
</td>
<td>
<input class="taskText" type="text"/>
</td>
</tr>
</table>
<input type="button" id="buttonAddRow" value="Add Row" />
</form>
答案 4 :(得分:1)
我赞成了关于Angular / Knokout / React的答案 - 这实际上是我强烈建议在实际应用中使用的。
就像练习一样,如果你使用一个简单的模板系统,这就是代码的样子。
这个想法是你不应该“手动”构建javascript,而是在HTML中声明模板。例如,项目选择可以如下所示:
<select class="project" name="">
<option value="">Select One</option>
<option class='project-option-tpl' value="{id}" style='display:none'>{project_no}</o
</select>
此处option
是项目选项的隐形模板。它对整个“行”和“任务”选择的工作方式类似,这里是实现逻辑的完整代码:
function createRow() {
var $clone = template($('.myrow-template')).clone();
template($clone.find('.project-option-tpl')).populate(myJson.listItems);
updateHours();
}
function updateHours() {
var total = 0;
$('.hours:visible').each(function (index, item) {
total += parseFloat($(item).val()) || 0;
});
$('#total').val(total);
}
$(function() {
createRow(); // create the first row
$('#mytable').on('change', '.project', function() {
// handle project change - get tasks for the selected project
// and populate the tasks template
var data = $(this).find(':selected').data('template-data');
template($(this).parent().parent().find('.task-option-tpl')).populate(data.task);
});
$('#mytable').on('change', '.task', function() {
// task change - update the task description
var data = $(this).find(':selected').data('template-data');
$(this).parent().parent().find('.task-text').val(data.task_description);
});
$('#mytable').on('mouseup', '.hours',function() {
updateHours(); // re-calculate total hours
});
$('.add-row').on('click', function() {
createRow(); // add one more row
});
});
“魔术”在template
函数中实现:
var template = function($target) {
$target = $($target.get(0));
return {
populate: function(data) {
// for each item in the data array - clone and populate the
// item template
var self = this;
this.clear();
$.each(data, function (index, value) {
self.clone(value);
});
},
clone: function(value) {
// clone a template for a single item and populate it with data
var $clone = $target.clone();
$clone.addClass('clone').appendTo($target.parent()).fadeIn('slow');
if (value) {
var html = $clone.get(0).outerHTML;
for (var key in value) {
html = html.replace('{'+key+'}', value[key]);
}
$clone.get(0).outerHTML = html;
$clone = $target.parent().find(':last')
$clone.data('template-data', value);
}
return $clone;
},
clear: function() {
// remove cloned templates
$target.parent().find('.clone').remove();
}
};
};
这是完整的可运行示例:
var myJson =
{
"listItems":[
{
"id":"1",
"project_no":"1001",
"task":[
{
"task_description":"Folding stuff",
"id":"111",
"task_summary":"Folding",
},
{
"task_description":"Drawing stuff",
"id":"222",
"task_summary":"Drawing"
}
]
},
{
"id":"2",
"project_no":"1002",
"task":[
{
"task_description":"Meeting description",
"id":"333",
"task_summary":"Meeting"
},
{
"task_description":"Administration",
"id":"444",
"task_summary":"Admin"
}
]
}
]
}
var template = function($target) {
$target = $($target.get(0));
return {
populate: function(data) {
var self = this;
this.clear();
$.each(data, function (index, value) {
self.clone(value);
});
},
clone: function(value) {
var $clone = $target.clone();
$clone.addClass('clone').appendTo($target.parent()).fadeIn('slow');
if (value) {
var html = $clone.get(0).outerHTML;
for (var key in value) {
html = html.replace('{'+key+'}', value[key]);
}
$clone.get(0).outerHTML = html;
$clone = $target.parent().find(':last')
$clone.data('template-data', value);
}
return $clone;
},
clear: function() {
$target.parent().find('.clone').remove();
}
};
};
function createRow() {
var $clone = template($('.myrow-template')).clone();
template($clone.find('.project-option-tpl')).populate(myJson.listItems);
updateHours();
}
function updateHours() {
var total = 0;
$('.hours:visible').each(function (index, item) {
total += parseFloat($(item).val()) || 0;
});
$('#total').val(total);
}
$(function(){
createRow();
$('#mytable').on('change', '.project', function() {
var data = $(this).find(':selected').data('template-data');
template($(this).parent().parent().find('.task-option-tpl')).populate(data.task);
});
$('#mytable').on('change', '.task', function() {
var data = $(this).find(':selected').data('template-data');
$(this).parent().parent().find('.task-text').val(data.task_description);
});
$('#mytable').on('mouseup', '.hours',function() {
updateHours();
});
$('.add-row').on('click', function() {
createRow();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form>
<table id='mytable'>
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input size=3 id='total' disabled='disabled'/></td>
<td></td>
</tr>
<tr class='myrow-template' style='display:none'>
<td>
<select class="project" name="">
<option value="">Select One</option>
<option class='project-option-tpl' value="{id}" style='display:none'>{project_no}</option>
</select>
</td>
<td>
<select class="task" name="">
<option value="000">-Select Task-</option>
<option class='task-option-tpl' value="{id}" data-description="{task_description}" style='display:none'>{task_summary}</option>
</select>
</td>
<td>
<select class='hours'>
<option>1.0</option>
<option>1.5</option>
<option>2.0</option>
</select>
</td>
<td><input type="text" value="" class="task-text" /></td>
</tr>
</table>
<input type="button" class="add-row" value="Add Row" />
</form>
答案 5 :(得分:1)
而angular.js版本,这里是完整的应用程序代码,其余部分在HTML中声明:
angular.module('MyApp', [])
.controller('TasksController', function() {
var self = this;
this.newRow = function() {
return { project: null, task: null, hours: 1.0 };
};
this.total = 0;
this.hours = [1.0, 1.5, 2.0];
this.projects = projects;
this.rows = [ this.newRow() ];
this.total = function() {
return this.rows.reduce(function(prev, curr) {
return prev + parseFloat(curr.hours);
}, 0);
};
this.addRow = function() {
this.rows.push(this.newRow());
};
});
完整代码:
var projects = [{
"id":"1",
"project_no":"1001",
"task":[ {
"task_description":"Folding stuff",
"id":"111",
"task_summary":"Folding",
}, {
"task_description":"Drawing stuff",
"id":"222",
"task_summary":"Drawing"
} ]
}, {
"id":"2",
"project_no":"1002",
"task":[ {
"task_description":"Meeting description",
"id":"333",
"task_summary":"Meeting"
}, {
"task_description":"Administration",
"id":"444",
"task_summary":"Admin"
} ]
}];
angular.module('MyApp', [])
.controller('TasksController', function() {
var self = this;
this.newRow = function() {
return { project: null, task: null, hours: 1.0 };
};
this.total = 0;
this.hours = [1.0, 1.5, 2.0];
this.projects = projects;
this.rows = [ this.newRow() ];
this.total = function() {
return this.rows.reduce(function(prev, curr) {
return prev + parseFloat(curr.hours);
}, 0);
};
this.addRow = function() {
this.rows.push(this.newRow());
};
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="MyApp">
<form ng-controller="TasksController as ctrl">
<table id='mytable'>
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input size=3 id='total' value="{{ctrl.total()}}" disabled='disabled'/></td>
<td></td>
</tr>
<tr ng-repeat="row in ctrl.rows">
<td>
<select ng-model="row.project" ng-options="project as project.project_no for project in ctrl.projects">
<option value="">Select One</option>
</select>
</td>
<td>
<select ng-model="row.task" ng-options="task as task.task_summary for task in row.project.task">
<option value="">Select Task</option>
</select>
</td>
<td>
<select ng-model='row.hours' ng-options="hour as (hour | number : 1) for hour in ctrl.hours">
</select>
</td>
<td><input type="text" value="{{row.task.task_description}}" class="task-text" /></td>
</tr>
</table>
<input type="button" ng-click="ctrl.addRow()" value="Add Row" />
</form>
</body>
&#13;
答案 6 :(得分:1)
这是一个使用普通javascript的版本,它不需要/使用元素id,将克隆存储为变量,禁用任务选择,除非选择了项目。
这个代码示例当然可以优化,包含在类等中,尽管我选择不这样做,以便尽可能简单地遵循代码流并查看正在进行的操作。
var myJson =
{
"listItems":[
{
"id":"1",
"project_no":"1001",
"task":[
{
"task_description":"Folding stuff",
"id":"111",
"task_summary":"Folding",
},
{
"task_description":"Drawing stuff",
"id":"222",
"task_summary":"Drawing"
}
]
},
{
"id":"2",
"project_no":"1002",
"task":[
{
"task_description":"Meeting description",
"id":"333",
"task_summary":"Meeting"
},
{
"task_description":"Administration",
"id":"444",
"task_summary":"Admin"
}
]
}
]
}
var template = function(target) {
return {
clone: function(value) {
if (!template.clone_item) {
// create clone variable
template.clone_item = target.cloneNode(true);
return target;
} else {
// return/append clone variable
return target.parentNode.appendChild(template.clone_item.cloneNode(true));
}
},
init: function(value) {
// first select (projects)
var sel = target.querySelector('select');
sel.addEventListener('change', function() {
// second select (tasks)
var sel = target.querySelectorAll('select')[1];
sel.addEventListener('change', function() {
var selvalues = this.options[this.selectedIndex].value.split('|');
var data = value[selvalues[0]].task[selvalues[1]].task_description;
// last inout (tasks descript.)
var inp = target.querySelector('input');
inp.value = data;
});
// clear last used task select options
for (i=sel.length-1;sel.length >1;i--) {
sel.remove(i);
}
// clear last used task descript.
var inp = target.querySelector('input');
inp.value = '';
// disable task select
sel.disabled = true;
// add task select options
var selvalue = this.options[this.selectedIndex].value;
if (selvalue != '') {
sel.disabled = false;
var data = value[selvalue].task;
var index = 0;
for (var key in data) {
createOption(sel,selvalue + '|' + index++,data[key].task_summary);
}
}
});
var index = 0;
// add project select options
for (var key in value) {
createOption(sel,index++,value[key].project_no);
}
// hours
var inp = target.querySelector('.hours');
inp.addEventListener('change', function() {
updateHours();
});
updateHours();
}
};
};
function createRow() {
var clone = template(document.querySelector('.myrow')).clone();
template(clone).init(myJson.listItems);
}
function createOption(sel,val,txt) {
var opt = document.createElement('option');
opt.value = val;
opt.text = txt;
sel.add(opt);
}
function updateHours() {
var total = 0;
var hours = document.querySelectorAll('#mytable .hours');
for (i = 0; i < hours.length;i++) {
total += parseFloat(hours[i].value) || 0;
}
document.getElementById('total').value = total;
}
function ready(fn) {
if (document.readyState != 'loading'){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function(){
createRow();
document.querySelector('.add-row').addEventListener('click', function() {
createRow();
});
});
&#13;
<form>
<table id='mytable'>
<tr>
<td>Project</td>
<td>Workstage</td>
<td>Hours</td>
<td>Description</td>
</tr>
<tr>
<td></td>
<td></td>
<td><input size=3 id='total' disabled='disabled'/></td>
<td></td>
</tr>
<tr class='myrow'>
<td>
<select class="project" name="">
<option value="">Select One</option>
</select>
</td>
<td>
<select class="task" name="" disabled=disabled>
<option value="000">-Select Task-</option>
</select>
</td>
<td>
<select class='hours'>
<option>1.0</option>
<option>1.5</option>
<option>2.0</option>
</select>
</td>
<td><input type="text" value="" class="task-text" /></td>
</tr>
</table>
<input type="button" class="add-row" value="Add Row" />
</form>
&#13;