百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>JS教程> 纯原生js实现table表格增删的教程
分享文章到:

纯原生js实现table表格增删的教程

发布时间:01/15 来源: 浏览: 关键词:
小编推荐的这篇文章介绍了纯原生js实现table表格增删的教程,有兴趣的同学可以参考一下

公司实习生问我table的增删操作,用jQuery很简单的实现了。又问我不使用jQuery,只使用js如何实现。

面对这种情况,我的一贯做法是‘不理解,但是支持'。

jQuery用的多了,人也懒了,但还是用js实现了这一操作,觉得难点在于IE兼容。。。

只是想找代码看看的可以跳过分析过程,文章底部附有完整代码。

以下是coding过程:

HTML结构代码

一个基本的table结构,增加了一些简单的样式,三个按钮分别对应创建、清空,和一个预留。

 
 代码如下
<!DOCTYPE HTML>
<html>
 <head>
  <title>table</title>
  <metacharset='utf-8'/>
  <styletype="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>
  <divid="main-content">
   <tableid="main-table"class="base">
    <thead>
     <tr>
      <thcolspan="3">This is a table for operations by javascript</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进行增删操作,可以先声明此全局变量

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,代码如下:

 
 代码如下
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;
  }
 }
}
 

优化修改

进行一些优化修改工作:

var elements = new Array();

修改为:var elements = [];

原因:数组用[]更好

将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'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">  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()方法中的?:格式修改为if else函数。

修改为:

 
 代码如下
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%的代码。对于实用主义的我而言,这一过程备受煎熬。不过还是从中有所收益的(违心。。)

以下为完整代码:

 
 代码如下
<!DOCTYPE HTML>
<html>
 <head>
  <title>table</title>
  <metacharset='utf-8'/>
  <styletype="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>
  <divid="main-content">
   <tableid="main-table"class="base">
    <thead>
     <tr>
      <thcolspan="3">This is a table for operations by javascript</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){
     var children = (element || document).getElementsByTagName('*');
     var elements = [];
     for (var i=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;
       }
      }
     }
     return elements;
    };
   }
   varvTbody=document.getElementById('main-table').getElementsByTagName('tbody')[0];
   function myInput(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);
    }
    return vInput;
   }
   function myTd(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);
    }
    return vTd;
   }
   function myTr(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);
    }
    return vTr;
   }
   function createTr() {
    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();
   }
   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() {
    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;
    }
   }
   function delTr() {
    varvTr=this.parentElement.parentElement;
    vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName('thead')[0].rows.length);
    reSequence();
   }
   function addBtnCpsListener() {
    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;
    }
   }
   function copyTr() {
    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>
 
打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有4人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板