纯原生js实现table表格的增删
公司实习生问我table的增删操作,用jquery很简单的实现了。又问我不使用jquery,只使用js如何实现。
面对这种情况,我的一贯做法是‘不理解,但是支持'。
jquery用的多了,人也懒了,但还是用js实现了这一操作,觉得难点在于ie兼容。。。
只是想找代码看看的可以跳过分析过程,文章底部附有完整代码。
以下是coding过程:
html结构代码
一个基本的table结构,增加了一些简单的样式,三个按钮分别对应创建、清空,和一个预留。
<!doctype html> <html> <head> <title>table</title> <meta charset='utf-8' /> <style type="text/css"> table.base{ border-collapse:collapse; text-align: center; border: 1px solid black; } table, tr, td, th{ border: 1px solid black; } </style> </head> <body> <div id="main-content"> <table id="main-table" class="base"> <thead> <tr> <th colspan="3">this is a table for operations by javascript</th> </tr> <tr> <th> <input type="button" value="create" id="cp_btn" onclick="createtr()" /> </th> <th> <input type="button" value="clear" id="cl_btn" onclick="cleartrs()" /> </th> <th> <input type="button" value="guess" id="cl_btn"/> </th> </tr> </thead> <tbody> </tbody> </table> </div> </body> </html>
构造函数(伪构造函数)
考虑过,创建一个隐藏的tr,基于此tr执行创建操作。为了不破坏html整体结构,决定通过js生成tr对象并append到页面中。
为了在页面加载完成后,再执行dom操作,所以将<script>放在代码下端</body>之前。
基于table中的tbody进行增删操作,可以先声明此全局变量
var vtbody = document.getelementbyid('main-table').getelementsbytagname('tbody')[0];
创建对象,可以使用document.createelement方法。
以面向对象的方式进行编程,先写构造函数(其实并不是标准的构造函数格式),从最内部的元素开始。
td中可能会有text和button等表单元素,所以先创建一个 input 的构造函数function myinput(vid, vclass, vtype, vvalue, vparent){}
这里有一个兼容性问题,就是ie内核不支持setattribute(class, value),需要使用setattribute(classname, value),所以为了解决兼容问题,可以通过
setattribute(class, value) for ff、chrome..
setattribute(classname, value) for ie
这里采用的是另一种方式 .classname,代码如下:
function myinput(vid, vclass, vtype, vvalue, vparent) { var vinput = document.createelement('input'); if(vid) { vinput.setattribute('id', vid); } vinput.setattribute('type', vtype); vinput.setattribute('value', vvalue); vinput.classname = vclass; if(vparent) { vparent.appendchild(vinput); } }
然后是td对象和tr对象的构造函数,大同小异,代码如下
function mytd(vid, vclass, vchild, vparent) { var vtd = document.createelement('td'); if(vid){ vtd.setattribute('id', vid); } vtd.classname = vclass; if(vchild) { vtd.appendchild(vchild); } if(vparent) { vparent.appendchild(vtd); } return vtd; } function mytr(vid, vclass, vchild, vparent) { var vtr = document.createelement('tr'); if(vid){ vtr.setattribute('id', vid); } vtr.classname = vclass; if(vchild) { vtr.appendchild(vchild); } if(vparent) { vparent.appendchild(vtr); } return vtr; }
新建行方法createtr()
构造函数完成之后,完善createtr()方法。
预想的tr结构为 序号,文本框,操作按钮。
依次创建相关对象。序号列需要动态刷新,所以先设定class名称,通过方法执行排序操作。
function createtr() { var vtr = new mytr(null, null, null, vtbody); //序列td var vtdseq = new mytd(null, 'seq', null, vtr); //文本框td var vtdtext = new mytd(null, null, null, vtr); var vinputtext = new myinput(null, 'td-inp-txt', 'text', '', vtdtext); //操作按钮td var vtdbtn = new mytd(null, null, null, vtr); var vinputbtncp = new myinput(null, 'td-inp-btn-cp', 'button', 'copy', vtdbtn); var vinputbtndel = new myinput(null, 'td-inp-btn-del', 'button', 'delete', vtdbtn); }
排序方法resequence()
创建一个动态排序方法resequence() ,有一个兼容性问题 innertext在火狐下无效果,所以使用innerhtml。代码如下
function resequence() { var vobj = vtbody.getelementsbyclassname('seq'); for (var i=0, len=vobj.length; i<len; i++) { vobj[i].innerhtml = i+1; } }
有一个兼容性问题,ie8及以下不支持getelementsbyclassname()方法,网上找到了解决方案
if(!document.getelementsbyclassname){ document.getelementsbyclassname = function(classname, element){ var children = (element || document).getelementsbytagname('*'); var elements = new array(); for (var i=0; i<children.length; i++){ var child = children[i]; var classnames = child.classname.split(' '); for (var j=0; j<classnames.length; j++){ if (classnames[j] == classname){ elements.push(child); break; } } } return elements; }; }
试图在object或者是htmltablesectionelement的原型上增加此方法,如
htmltablesectionelement.prototype.getelementsbyclassname = function(){}
可惜没有实现。
修改后的代码为
function resequence() { var vobj = vtbody.getelementsbyclassname == null?document.getelementsbyclassname('seq', vtbody):vtbody.getelementsbyclassname('seq'); for (var i=0, len=vobj.length; i<len; i++) { vobj[i].innerhtml = i+1; } }
除了排序外,还需其他操作,所以我们创建一个init()方法,集中管理resequence()这些方法,在createtr()方法的结尾调用init()方法。
清空行方法cleartrs()
移除/销毁某个dom对象,首先想到的是remove()方法,不幸的是,存在ie浏览器兼容问题,因此,采用了一个更简便的方式,对dom对象执行innerhtml="",代码如下
function cleartrs() { vtbody.innerhtml = ''; }
ie8报错,'未知的运行错误'。查了以下,因为ie8的table.innerhtml是只读属性,妹的!再改:
function cleartrs() { while(vtbody.rows.length >0) { vtbody.deleterow(); } }
删除行方法addbtndelslistener()
接下来,给delete按钮绑定删除当前行的方法。
为了解决兼容性问题,网上给出的方法是针对不同浏览器(ie、非ie)分别使用addeventlistener、attachevent方法,
我采用的是另一种解决方案:
obj.onclick = function(){};
匿名函数的方法体,吸取了上面cleartrs()方法的经验教训,直接采用deleterow(index)方法。
有一点需要注意thistr.rowindex获取的行数,比当前行要大2,因为thead中还有两行。所以当前的索引数=thistr.rowindex-vthead.rows.length
代码如下:
function addbtndelslistener() { var vbtndels = vtbody.getelementsbyclassname == null?document.getelementsbyclassname('td-inp-btn-del', vtbody):vtbody.getelementsbyclassname('td-inp-btn-del'); for (var i=0, len=vbtndels.length; i<len; i++) { vbtndels[i].onclick = function() { var vtr = this.parentelement.parentelement; vtbody.deleterow(vtr.rowindex-vtbody.parentnode.getelementsbytagname('thead')[0].rows.length); resequence(); }; } }
执行完删除操作后,通过resequence()方法重新排序。
同时将addbtndelslistener()方法加入到init()方法中。
复制行方法addbtncpslistener()
再来看一下copy按钮,添加事件监听的方式同上。
如果innerhtml不是只读的话,可以createelement一个tr元素 然后newtr.innerhtml=thistr.innerhtml,
为了兼容性,必须做些改变。
其实可以将复制看做是新建,唯一的不同在于新建行的文本输入框的内容要等同于被复制行。
这就简单了。我可以先调用createtr()方法,再将最后一个元素lastchild中的文本框的value等于被复制行。
思路有了,代码如下:
function addbtncpslistener() { var vbtncps = vtbody.getelementsbyclassname == null?document.getelementsbyclassname('td-inp-btn-cp', vtbody):vtbody.getelementsbyclassname('td-inp-btn-cp'); for (var i=0, len=vbtncps.length; i<len; i++) { vbtncps[i].onclick = function() { createtr(); var vnewtr = vtbody.lastchild; var vtr = this.parentelement.parentelement; vnewtr.getelementsbyclassname == null?document.getelementsbyclassname('td-inp-txt', vnewtr)[0].value = document.getelementsbyclassname('td-inp-txt', vtr)[0].value:vnewtr.getelementsbyclassname('td-inp-txt')[0].value = vtr.getelementsbyclassname('td-inp-txt')[0].value; } } }
优化修改
进行一些优化修改工作:
var elements = new array();
修改为:var elements = [];
原因:数组用[]更好
将addbtndelslistener方法中的vbtndels[i].onclick = function() {
修改为:vbtndels[i].onclick = deltr;
外部新创建一个函数
function deltr() { var vtr = this.parentelement.parentelement; vtbody.deleterow(vtr.rowindex-vtbody.parentnode.getelementsbytagname('thead')[0].rows.length); resequence(); }
原因:don't make functions within a loop.
同理,将addbtncpslistener中的vbtncps[i].onclick = function() {
修改为:vbtncps[i].onclick = copytr;
外部新创建一个函数
<pre code_snippet_id="139791" snippet_file_name="blog_20140103_15_6784659" name="code" class="javascript"> function copytr() { createtr(); var vnewtr = vtbody.lastchild; var vtr = this.parentelement.parentelement; vnewtr.getelementsbyclassname === null? document.getelementsbyclassname('td-inp-txt', vnewtr)[0].value = document.getelementsbyclassname('td-inp-txt', vtr)[0].value: vnewtr.getelementsbyclassname('td-inp-txt')[0].value = vtr.getelementsbyclassname('td-inp-txt')[0].value; }</pre> <pre></pre> <pre></pre>
将copytr()方法中的?:格式修改为if else函数。
修改为:
function copytr() { createtr(); var vnewtr = vtbody.lastchild; var vtr = this.parentelement.parentelement; if(vnewtr.getelementsbyclassname) { vnewtr.getelementsbyclassname('td-inp-txt')[0].value = vtr.getelementsbyclassname('td-inp-txt')[0].value; } else { document.getelementsbyclassname('td-inp-txt', vnewtr)[0].value = document.getelementsbyclassname('td-inp-txt', vtr)[0].value; } }
原因:?:预期返回值应该是一个变量or函数,而不应该是一个表达式操作。
有一点需要注意:js最佳实现经常看到要使用===替换==。但是本示例中的==null,如果替换成===null会在ie8一下版本中出现问题。
完整代码
至此,一个完全基于原生javascript,并且兼容至ie6的table增删完成了。
还是想吐槽一下,如果不兼容ie10以下的版本,可以节省50%的代码。如果使用jquery,又可以节省50%的代码。对于实用主义的我而言,这一过程备受煎熬。不过还是从中有所收益的(违心。。)
以下为完整代码:
<!doctype html> <html> <head> <title>table</title> <meta charset='utf-8' /> <style type="text/css"> table.base{ border-collapse:collapse; text-align: center; border: 1px solid black; } table, tr, td, th{ border: 1px solid black; } </style> </head> <body> <div id="main-content"> <table id="main-table" class="base"> <thead> <tr> <th colspan="3">this is a table for operations by javascript</th> </tr> <tr> <th> <input type="button" value="create" id="cp_btn" onclick="createtr()" /> </th> <th> <input type="button" value="clear" id="cl_btn" onclick="cleartrs()" /> </th> <th> <input type="button" value="guess" id="cl_btn"/> </th> </tr> </thead> <tbody> </tbody> </table> </div> <script type="text/javascript"> if(!document.getelementsbyclassname){ document.getelementsbyclassname = function(classname, element){ var children = (element || document).getelementsbytagname('*'); var elements = []; for (var i=0; i<children.length; i++){ var child = children[i]; var classnames = child.classname.split(' '); for (var j=0; j<classnames.length; j++){ if (classnames[j] == classname){ elements.push(child); break; } } } return elements; }; } var vtbody = document.getelementbyid('main-table').getelementsbytagname('tbody')[0]; function myinput(vid, vclass, vtype, vvalue, vparent) { var vinput = document.createelement('input'); if(vid) { vinput.setattribute('id', vid); } vinput.setattribute('type', vtype); vinput.setattribute('value', vvalue); vinput.classname = vclass; if(vparent) { vparent.appendchild(vinput); } return vinput; } function mytd(vid, vclass, vchild, vparent) { var vtd = document.createelement('td'); if(vid){ vtd.setattribute('id', vid); } vtd.classname = vclass; if(vchild) { vtd.appendchild(vchild); } if(vparent) { vparent.appendchild(vtd); } return vtd; } function mytr(vid, vclass, vchild, vparent) { var vtr = document.createelement('tr'); if(vid){ vtr.setattribute('id', vid); } vtr.classname = vclass; if(vchild) { vtr.appendchild(vchild); } if(vparent) { vparent.appendchild(vtr); } return vtr; } function createtr() { var vtr = new mytr(null, null, null, vtbody); //序列td var vtdseq = new mytd(null, 'seq', null, vtr); //文本框td var vtdtext = new mytd(null, null, null, vtr); var vinputtext = new myinput(null, 'td-inp-txt', 'text', '', vtdtext); //操作按钮td var vtdbtn = new mytd(null, null, null, vtr); var vinputbtncp = new myinput(null, 'td-inp-btn-cp', 'button', 'copy', vtdbtn); var vinputbtndel = new myinput(null, 'td-inp-btn-del', 'button', 'delete', vtdbtn); init(); } function cleartrs() { while(vtbody.rows.length >0) { vtbody.deleterow(); } } function init(){ resequence(); addbtndelslistener(); addbtncpslistener(); } function resequence() { var vobj = vtbody.getelementsbyclassname == null? document.getelementsbyclassname('seq', vtbody): vtbody.getelementsbyclassname('seq'); for (var i=0, len=vobj.length; i<len; i++) { vobj[i].innerhtml = i+1; } } function addbtndelslistener() { var vbtndels = vtbody.getelementsbyclassname == null? document.getelementsbyclassname('td-inp-btn-del', vtbody): vtbody.getelementsbyclassname('td-inp-btn-del'); for (var i=0, len=vbtndels.length; i<len; i++) { vbtndels[i].onclick = deltr; } } function deltr() { var vtr = this.parentelement.parentelement; vtbody.deleterow(vtr.rowindex-vtbody.parentnode.getelementsbytagname('thead')[0].rows.length); resequence(); } function addbtncpslistener() { var vbtncps = vtbody.getelementsbyclassnamenull == null? document.getelementsbyclassname('td-inp-btn-cp', vtbody): vtbody.getelementsbyclassname('td-inp-btn-cp'); for (var i=0, len=vbtncps.length; i<len; i++) { vbtncps[i].onclick = copytr; } } function copytr() { createtr(); var vnewtr = vtbody.lastchild; var vtr = this.parentelement.parentelement; if(vnewtr.getelementsbyclassname) { vnewtr.getelementsbyclassname('td-inp-txt')[0].value = vtr.getelementsbyclassname('td-inp-txt')[0].value; } else { document.getelementsbyclassname('td-inp-txt', vnewtr)[0].value = document.getelementsbyclassname('td-inp-txt', vtr)[0].value; } } </script> </body> </html>
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!
上一篇: 中国人的肠胃该多吃点素
下一篇: 给我爸上个小姐