纯原生js实现table表格的增删
公司实习生问我table的增删操作,用jQuery很简单的实现了。又问我不使用jQuery,只使用js如何实现。
面对这种情况,我的一贯做法是‘不理解,但是支持'。
jQuery用的多了,人也懒了,但还是用js实现了这一操作,觉得难点在于IE兼容。。。
只是想找代码看看的可以跳过分析过程,文章底部附有完整代码。
以下是coding过程:
HTML结构代码
一个基本的table结构,增加了一些简单的样式,三个按钮分别对应创建、清空,和一个预留。
<!DOCTYPEHTML>
<html>
<head>
<title>table</title>
<metacharset='utf-8'/>
<styletype="text/css">
table.base{
border-collapse:collapse;
text-align:center;
border:1pxsolidblack;
}
table,tr,td,th{
border:1pxsolidblack;
}
</style>
</head>
<body>
<divid="main-content">
<tableid="main-table"class="base">
<thead>
<tr>
<thcolspan="3">Thisisatableforoperationsbyjavascript</th>
</tr>
<tr>
<th>
<inputtype="button"value="CREATE"id="cp_btn"onclick="createTr()"/>
</th>
<th>
<inputtype="button"value="CLEAR"id="cl_btn"onclick="clearTrs()"/>
</th>
<th>
<inputtype="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进行增删操作,可以先声明此全局变量
varvTbody=document.getElementById('main-table').getElementsByTagName('tbody')[0];
创建对象,可以使用document.createElement方法。
以面向对象的方式进行编程,先写构造函数(其实并不是标准的构造函数格式),从最内部的元素开始。
td中可能会有text和button等表单元素,所以先创建一个input的构造函数functionmyInput(vId,vClass,vType,vValue,vParent){}
这里有一个兼容性问题,就是IE内核不支持setAttribute(class,value),需要使用setAttribute(className,value),所以为了解决兼容问题,可以通过
setAttribute(class,value)forFF、Chrome..
setAttribute(className,value)forIE
这里采用的是另一种方式.className,代码如下:
functionmyInput(vId,vClass,vType,vValue,vParent){
varvInput=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对象的构造函数,大同小异,代码如下
functionmyTd(vId,vClass,vChild,vParent){
varvTd=document.createElement('td');
if(vId){
vTd.setAttribute('id',vId);
}
vTd.className=vClass;
if(vChild){
vTd.appendChild(vChild);
}
if(vParent){
vParent.appendChild(vTd);
}
returnvTd;
}
functionmyTr(vId,vClass,vChild,vParent){
varvTr=document.createElement('tr');
if(vId){
vTr.setAttribute('id',vId);
}
vTr.className=vClass;
if(vChild){
vTr.appendChild(vChild);
}
if(vParent){
vParent.appendChild(vTr);
}
returnvTr;
}
新建行方法createTr()
构造函数完成之后,完善createTr()方法。
预想的tr结构为序号,文本框,操作按钮。
依次创建相关对象。序号列需要动态刷新,所以先设定class名称,通过方法执行排序操作。
functioncreateTr(){
varvTr=newmyTr(null,null,null,vTbody);
//序列td
varvTdSeq=newmyTd(null,'seq',null,vTr);
//文本框td
varvTdText=newmyTd(null,null,null,vTr);
varvInputText=newmyInput(null,'td-inp-txt','text','',vTdText);
//操作按钮td
varvTdBtn=newmyTd(null,null,null,vTr);
varvInputBtnCp=newmyInput(null,'td-inp-btn-cp','button','COPY',vTdBtn);
varvInputBtnDel=newmyInput(null,'td-inp-btn-del','button','DELETE',vTdBtn);
}
排序方法reSequence()
创建一个动态排序方法reSequence(),有一个兼容性问题innerText在火狐下无效果,所以使用innerHTML。代码如下
functionreSequence(){
varvObj=vTbody.getElementsByClassName('seq');
for(vari=0,len=vObj.length;i<len;i++){
vObj[i].innerHTML=i+1;
}
}
有一个兼容性问题,IE8及以下不支持getElementsByClassName()方法,网上找到了解决方案
if(!document.getElementsByClassName){
document.getElementsByClassName=function(className,element){
varchildren=(element||document).getElementsByTagName('*');
varelements=newArray();
for(vari=0;i<children.length;i++){
varchild=children[i];
varclassNames=child.className.split('');
for(varj=0;j<classNames.length;j++){
if(classNames[j]==className){
elements.push(child);
break;
}
}
}
returnelements;
};
}
试图在Object或者是HTMLTableSectionElement的原型上增加此方法,如
HTMLTableSectionElement.prototype.getElementsByClassName=function(){}
可惜没有实现。
修改后的代码为
functionreSequence(){
varvObj=vTbody.getElementsByClassName==null?document.getElementsByClassName('seq',vTbody):vTbody.getElementsByClassName('seq');
for(vari=0,len=vObj.length;i<len;i++){
vObj[i].innerHTML=i+1;
}
}
除了排序外,还需其他操作,所以我们创建一个init()方法,集中管理reSequence()这些方法,在createTr()方法的结尾调用init()方法。
清空行方法clearTrs()
移除/销毁某个dom对象,首先想到的是remove()方法,不幸的是,存在IE浏览器兼容问题,因此,采用了一个更简便的方式,对dom对象执行innerHTML="",代码如下
functionclearTrs(){
vTbody.innerHTML='';
}
IE8报错,'未知的运行错误'。查了以下,因为ie8的table.innerHTML是只读属性,妹的!再改:
functionclearTrs(){
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
代码如下:
functionaddBtnDelsListener(){
varvBtnDels=vTbody.getElementsByClassName==null?document.getElementsByClassName('td-inp-btn-del',vTbody):vTbody.getElementsByClassName('td-inp-btn-del');
for(vari=0,len=vBtnDels.length;i<len;i++){
vBtnDels[i].onclick=function(){
varvTr=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等于被复制行。
思路有了,代码如下:
functionaddBtnCpsListener(){
varvBtnCps=vTbody.getElementsByClassName==null?document.getElementsByClassName('td-inp-btn-cp',vTbody):vTbody.getElementsByClassName('td-inp-btn-cp');
for(vari=0,len=vBtnCps.length;i<len;i++){
vBtnCps[i].onclick=function(){
createTr();
varvNewTr=vTbody.lastChild;
varvTr=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;
}
}
}
优化修改
进行一些优化修改工作:
varelements=newArray();
修改为:varelements=[];
原因:数组用[]更好
将addBtnDelsListener方法中的vBtnDels[i].onclick=function(){
修改为:vBtnDels[i].onclick=delTr;
外部新创建一个函数
functiondelTr(){
varvTr=this.parentElement.parentElement;
vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName('thead')[0].rows.length);
reSequence();
}
原因:Don'tmakefunctionswithinaloop.
同理,将addBtnCpsListener中的vBtnCps[i].onclick=function(){
修改为:vBtnCps[i].onclick=copyTr;
外部新创建一个函数
<precode_snippet_id="139791"snippet_file_name="blog_20140103_15_6784659"name="code"class="javascript">functioncopyTr(){
createTr();
varvNewTr=vTbody.lastChild;
varvTr=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()方法中的?:格式修改为ifelse函数。
修改为:
functioncopyTr(){
createTr();
varvNewTr=vTbody.lastChild;
varvTr=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%的代码。对于实用主义的我而言,这一过程备受煎熬。不过还是从中有所收益的(违心。。)
以下为完整代码:
<!DOCTYPEHTML>
<html>
<head>
<title>table</title>
<metacharset='utf-8'/>
<styletype="text/css">
table.base{
border-collapse:collapse;
text-align:center;
border:1pxsolidblack;
}
table,tr,td,th{
border:1pxsolidblack;
}
</style>
</head>
<body>
<divid="main-content">
<tableid="main-table"class="base">
<thead>
<tr>
<thcolspan="3">Thisisatableforoperationsbyjavascript</th>
</tr>
<tr>
<th>
<inputtype="button"value="CREATE"id="cp_btn"onclick="createTr()"/>
</th>
<th>
<inputtype="button"value="CLEAR"id="cl_btn"onclick="clearTrs()"/>
</th>
<th>
<inputtype="button"value="GUESS"id="cl_btn"/>
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<scripttype="text/javascript">
if(!document.getElementsByClassName){
document.getElementsByClassName=function(className,element){
varchildren=(element||document).getElementsByTagName('*');
varelements=[];
for(vari=0;i<children.length;i++){
varchild=children[i];
varclassNames=child.className.split('');
for(varj=0;j<classNames.length;j++){
if(classNames[j]==className){
elements.push(child);
break;
}
}
}
returnelements;
};
}
varvTbody=document.getElementById('main-table').getElementsByTagName('tbody')[0];
functionmyInput(vId,vClass,vType,vValue,vParent){
varvInput=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);
}
returnvInput;
}
functionmyTd(vId,vClass,vChild,vParent){
varvTd=document.createElement('td');
if(vId){
vTd.setAttribute('id',vId);
}
vTd.className=vClass;
if(vChild){
vTd.appendChild(vChild);
}
if(vParent){
vParent.appendChild(vTd);
}
returnvTd;
}
functionmyTr(vId,vClass,vChild,vParent){
varvTr=document.createElement('tr');
if(vId){
vTr.setAttribute('id',vId);
}
vTr.className=vClass;
if(vChild){
vTr.appendChild(vChild);
}
if(vParent){
vParent.appendChild(vTr);
}
returnvTr;
}
functioncreateTr(){
varvTr=newmyTr(null,null,null,vTbody);
//序列td
varvTdSeq=newmyTd(null,'seq',null,vTr);
//文本框td
varvTdText=newmyTd(null,null,null,vTr);
varvInputText=newmyInput(null,'td-inp-txt','text','',vTdText);
//操作按钮td
varvTdBtn=newmyTd(null,null,null,vTr);
varvInputBtnCp=newmyInput(null,'td-inp-btn-cp','button','COPY',vTdBtn);
varvInputBtnDel=newmyInput(null,'td-inp-btn-del','button','DELETE',vTdBtn);
init();
}
functionclearTrs(){
while(vTbody.rows.length>0){
vTbody.deleteRow();
}
}
functioninit(){
reSequence();
addBtnDelsListener();
addBtnCpsListener();
}
functionreSequence(){
varvObj=vTbody.getElementsByClassName==null?
document.getElementsByClassName('seq',vTbody):
vTbody.getElementsByClassName('seq');
for(vari=0,len=vObj.length;i<len;i++){
vObj[i].innerHTML=i+1;
}
}
functionaddBtnDelsListener(){
varvBtnDels=vTbody.getElementsByClassName==null?
document.getElementsByClassName('td-inp-btn-del',vTbody):
vTbody.getElementsByClassName('td-inp-btn-del');
for(vari=0,len=vBtnDels.length;i<len;i++){
vBtnDels[i].onclick=delTr;
}
}
functiondelTr(){
varvTr=this.parentElement.parentElement;
vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName('thead')[0].rows.length);
reSequence();
}
functionaddBtnCpsListener(){
varvBtnCps=vTbody.getElementsByClassNamenull==null?
document.getElementsByClassName('td-inp-btn-cp',vTbody):
vTbody.getElementsByClassName('td-inp-btn-cp');
for(vari=0,len=vBtnCps.length;i<len;i++){
vBtnCps[i].onclick=copyTr;
}
}
functioncopyTr(){
createTr();
varvNewTr=vTbody.lastChild;
varvTr=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>
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持毛票票!