下图只是一个简单的demo,没有复杂的样式表:
为了灵活对应不同的表格,提取了一个共通的 js 来处理,作为使用前提:
1. table 必须有 id;
2. 有 id 的 tr 才会被复制;(tr的id从1开始编号)
3. table 内所有id都必须以 xxx_n 编号
function rowcopyutility(opts) { // 表格id this.tableid = opts.tableid; // 分组内有多少行 this.rowgroupnumber = opts.rowgroupnumber; // 一组内button对应的方法map(key=button value, value=对应方法名) // 所有方法都应以 function (idx) 方式调用 this.buttonhandlers = opts.buttonhandlers; this._countforrowsgroup = -1; this._keyforrow = -1; this.gettargetrowgroup = function(groupidx) { var rows = []; if (groupidx > 0) { for(var i=1; i<this.rowgroupnumber+1; i++) { rows[i-1] = $("#row" + i + "_" + groupidx); } } else { for(var i=0; i<this.rowgroupnumber; i++) { rows[i] = $("#" + this.tableid + " tr[id]").eq(i); } } return rows; }; this.addrow = function (groupidx, needcopyvalue) { if (this._countforrowsgroup == -1) { this._countforrowsgroup = ($("#" + this.tableid + " tr[id]").length - 1)/this.rowgroupnumber; this._keyforrow = parseint($("#" + this.tableid + " tr[id]:not(#row_add):last").attr("id").split("_")[1]) + 1; } if (groupidx == 0) { var firstrow = $("#" + this.tableid + " tr[id]:first"); var currentidx = firstrow.attr("id").split("_")[1]; groupidx = currentidx; } var regforid = new regexp("^(\\w+_)" + groupidx + "$"); var regforname = new regexp("^(\\w+_)" + groupidx + "$"); var regforradioid = new regexp("^(\\w+_)" + groupidx + "(.*)$"); var targetrows = this.gettargetrowgroup(groupidx); // 重要:注意闭包参数的作用域 var idx = this._keyforrow; for(var i=0; i<targetrows.length; i++) { // clone target rows var clonerow = targetrows[i].clone(false); var newrowid = clonerow.attr("id").split("_")[0] + "_" + idx; clonerow.attr("id", newrowid); var radios = []; clonerow.find("[id]").each(function() { var id = $(this).attr("id"); var oldid = id; var name = $(this).attr("name"); id = id.replace(regforid, "$1" + idx); $(this).attr("id", id); var newname = name.replace(regforname, "$1" + idx); $(this).attr("name", newname); if ($(this).hasclass("hasdatepicker")) { $(this).removeclass("hasdatepicker"); } if ($(this).attr("type") == "checkbox") { if($(this).next().attr("for") != "") { $(this).next().attr("for", id); } if (!needcopyvalue) { $(this).attr("checked", ""); } } else if ($(this).attr("type") == "radio") { id = id.replace(regforradioid, "$1" + idx); $(this).attr("id", id); var radio = new object(); radio.id = id; radio.oldid = oldid; radio.name = name; radio.newname = newname; // ie7's bug radio.checked = document.getelementbyid(oldid).checked; radios[radios.length] = radio; if($(this).next().attr("for") != "") { $(this).next().attr("for", id); } if (!needcopyvalue) { $(this).attr("checked", ""); } } else if ($(this).attr("tagname") == "select") { if (needcopyvalue) { $(this).val(document.getelementbyid(oldid).value); } } else if ($(this).attr("tagname") == "textarea" || $(this).attr("type") == "text" || $(this).attr("type") == "hidden") { if (!needcopyvalue) { $(this).val(""); } } }); // insert into document clonerow.insertbefore("#" + this.tableid + " tr:last"); // replace name for radio for(var n=0; n<radios.length; n++) { document.getelementbyid(radios[n].id).outerhtml = document.getelementbyid(radios[n].id).outerhtml.replace(radios[n].name, radios[n].newname); // ie7's bug document.getelementbyid(radios[n].oldid).checked = radios[n].checked; } // event handler var maps = this.buttonhandlers; clonerow.find("input:button").each(function() { var value = $(this).attr("value"); var funcname = maps[value]; if (funcname != undefined) { var func = null; func = function() { eval(funcname + "(" + idx + ")"); }; if (func != null) { $(this).attr("onclick", ""); $(this).unbind("click"); $(this).attr("onclick", "").click(func); } } }); } this._countforrowsgroup++; this._keyforrow++; }; this.copyrow = function(groupidx) { this.addrow(groupidx, true); }; this.deleterow = function(groupidx) { if (this._countforrowsgroup == -1) { this._countforrowsgroup = ($("#" + this.tableid + " tr[id]").length - 1)/this.rowgroupnumber; this._keyforrow = parseint($("#" + this.tableid + " tr[id]:not(#row_add):last").attr("id").split("_")[1]) + 1; } var allrows = $("#" + this.tableid + " tr[id]"); var minirowscount = this.rowgroupnumber + 1; var tbl = $("#" + this.tableid); if (allrows.length == minirowscount) { tbl.find("input:text").each(function() { $(this).val(""); }); tbl.find("textarea").each(function() { $(this).val(""); }); tbl.find("input:hidden").each(function() { $(this).val(""); }); tbl.find("input:radio").each(function() { $(this).attr("checked", ""); }); tbl.find("input:checkbox").each(function() { $(this).attr("checked", ""); }); tbl.find("select").each(function() { document.getelementbyid($(this).attr("id")).selectedindex = 0; }); tbl.find(".fg-common-field-errored").each(function() { $(this).removeclass("fg-common-field-errored"); }); return; } for(var i=1; i<this.rowgroupnumber+1; i++) { tbl.find("#row" + i + "_" + groupidx).remove(); } this._countforrowsgroup--; }; }
实际遇到的问题与解决办法:
1. jquery 的 clone() 方法,就算传入 false,元素的事件依然会被复制过来。(ie测试)
2. attr("name", name); 在ie中,不会直接替换掉,而是生成 submitname 保存。在 ie7 里 radio 会因为 name 相同而出现问题。
3. 在大量的匿名方法中,特别要注意闭包封送参数的作用域。
4. ie7里的bug:在radio被复制时,原来的元素的选择值就没了。因此在复制前保存了复制源的radio属性,加入document之后再次设定:
// replace name for radio for(var n=0; n<radios.length; n++) { document.getelementbyid(radios[n].id).outerhtml = document.getelementbyid(radios[n].id).outerhtml.replace(radios[n].name, radios[n].newname); // ie7's bug document.getelementbyid(radios[n].oldid).checked = radios[n].checked; }
5. jquery里清除事件单独用 attr("onclick", "") 并不好用;后期用 click(function) 绑定的事件用 unbind("click") 可以移除。
if (func != null) { $(this).attr("onclick", ""); $(this).unbind("click"); $(this).attr("onclick", "").click(func); }
6. jquery ui 的 datepicker 当创建了 datepicker 之后,可以通过 hasclass("hasdatepick") 判断是否存在,否则在复制之后有问题。
(多次复制之后 datepicker settings 会莫名其妙丢失)
7. 其他,剩下就是要注意 jquery 选择器不要过度使用了,越复杂的表达式效率越低。
顺便推荐看一下:15个值得开发人员关注的jquery开发技巧和心得
还要说下ie9 的 debug 工具真心不错,提高不少开发效率哦一定要利用。
就这些,希望能对大家有帮助。最后附上,测试用的 html:
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="cache-control" content="no-cache" /> <meta http-equiv="expires" content="0" /> <style> body{font-family:'open sans',arial,sans-serif;} tr{height:30px} input.button{width:60px} table.main { border-width: 2px; border-spacing: 1px; border-style: solid; border-color: gray; border-collapse: collapse; background-color: white; } table.main th { border-width: 1px; padding: 5px; border-style: inset; border-color: gray; background-color: #f0f0f0; -moz-border-radius: ; } table.main td { border-width: 1px; padding: 5px; border-style: inset; border-color: gray; background-color: white; -moz-border-radius: ; } </style> <script type="text/javascript" language="javascript" src="jquery.js"></script> <script type="text/javascript" language="javascript" src="jquery-ui.js"></script> <script type="text/javascript" language="javascript" src="rowcopyutil.js"></script> <link rel="stylesheet" href="jquery-ui.css" type="text/css" media="all" /> <link type="text/css" href="jquerycalendarstyle.css" rel="stylesheet" /> <script type="text/javascript" > var rowutil = new rowcopyutility( { tableid: "tab1", rowgroupnumber: 3, buttonhandlers: {"copy":"copyrows", "delete":"deleterows", "calendar":"showdatepicker", "some button":"somebuttonclick"} } ); function showdatepicker(idx) { var textid = "#calendar_" + idx; if (!$(textid).hasclass("hasdatepicker")) { var text = $(textid).datepicker({ showon : "calendar", dateformat : "yy/mm/dd" }); } $(textid).datepicker('show'); } function addrows() { rowutil.addrow(0, false); } function copyrows(idx) { rowutil.copyrow(idx); } function deleterows(idx) { rowutil.deleterow(idx); } function somebuttonclick(idx) { alert(idx); } </script> </head> <body> <table id="tab1" class="main"> <tr> <th>header1</th> <th>header2</th> <th>header3</th> <th>header4</th> </tr> <tr id="row1_0"> <td rowspan="3" > <input class="button" type="button" value="copy" onclick="copyrows(0);" /> <input class="button" type="button" value="delete" onclick="deleterows(0);" /> </td> <td>text:<input type="text" id="text_0" /></td> <td> <input type="radio" name="radioab_0" id="radioa_0" value="1" /><label for="radioa_0">raido_a </label> <input type="radio" name="radioab_0" id="radiob_0" value="2" /><label for="radiob_0">radio_b </label> </td> <td> <select id="select_0"> <option value="0">---select---</option> <option value="1">select option1</option> <option value="2">select option2</option> </select> </td> </tr> <tr id="row2_0"> <td> <input type="checkbox" id="checka_0" /><label for="checka_0">check_a </label> <input type="checkbox" id="checkb_0" /><label for="checkb_0">check_b </label> </td> <td colspan="2"> <input type="text" id="calendar_0" style="width:90px"/><input type="button" value="calendar" onclick="showdatepicker(0);" /> <input type="button" value="some button" onclick="somebuttonclick(0);" /> </td> </tr> <tr id="row3_0"> <td colspan="3"> textarea:<textarea id="textarea_0" style="width:100%"></textarea> </td> </tr> <tr id="row_add"> <td colspan="4"> <input class="button" type="button" value="add" onclick="addrows();" /> </td> </tr> </table> </body> </html>
以上就是利用 jquery clone进行复制操作的详细内容。
