公司实习生问我table的增删操作,用jQuery很简单的实现了。又问我不使用jQuery,只使用js如何实现。 面对这种情况,我的一贯做法是‘不理解,但是支持'。 jQuery用的多了,人也懒了,但还是用js实现了这一操作,觉得难点在于IE兼容。。。 只是想找代码看看的可以跳过分析过程,文章底部附有完整代码。 以下是coding过程: HTML结构代码 一个基本的table结构,增加了一些简单的样式,三个按钮分别对应创建、清空,和一个预留。
构造函数(伪构造函数) 考虑过,创建一个隐藏的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,代码如下:
然后是td对象和tr对象的构造函数,大同小异,代码如下
新建行方法createTr() 构造函数完成之后,完善createTr()方法。 预想的tr结构为 序号,文本框,操作按钮。 依次创建相关对象。序号列需要动态刷新,所以先设定class名称,通过方法执行排序操作。
排序方法reSequence() 创建一个动态排序方法reSequence() ,有一个兼容性问题 innerText在火狐下无效果,所以使用innerHTML。代码如下
有一个兼容性问题,IE8及以下不支持getElementsByClassName()方法,网上找到了解决方案
试图在Object或者是HTMLTableSectionElement的原型上增加此方法,如 HTMLTableSectionElement.prototype.getElementsByClassName = function(){} 可惜没有实现。 修改后的代码为
除了排序外,还需其他操作,所以我们创建一个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 代码如下:
执行完删除操作后,通过reSequence()方法重新排序。 同时将addBtnDelsListener()方法加入到init()方法中。 复制行方法addBtnCpsListener() 再来看一下COPY按钮,添加事件监听的方式同上。 如果innerHTML不是只读的话,可以createElement一个tr元素 然后newTr.innerHTML=thisTr.innerHTML, 为了兼容性,必须做些改变。 其实可以将复制看做是新建,唯一的不同在于新建行的文本输入框的内容要等同于被复制行。 这就简单了。我可以先调用createTr()方法,再将最后一个元素lastChild中的文本框的value等于被复制行。 思路有了,代码如下:
优化修改 进行一些优化修改工作: var elements = new Array(); 修改为:var elements = []; 原因:数组用[]更好 将addBtnDelsListener方法中的vBtnDels[i].onclick = function() { 修改为:vBtnDels[i].onclick = delTr; 外部新创建一个函数
原因:Don't make functions within a loop. 同理,将addBtnCpsListener中的vBtnCps[i].onclick = function() { 修改为:vBtnCps[i].onclick = copyTr; 外部新创建一个函数
将copyTr()方法中的?:格式修改为if else函数。 修改为:
原因:?:预期返回值应该是一个变量or函数,而不应该是一个表达式操作。 有一点需要注意:js最佳实现经常看到要使用===替换==。但是本示例中的==null,如果替换成===null会在ie8一下版本中出现问题。 完整代码 至此,一个完全基于原生JavaScript,并且兼容至IE6的table增删完成了。 还是想吐槽一下,如果不兼容IE10以下的版本,可以节省50%的代码。如果使用jQuery,又可以节省50%的代码。对于实用主义的我而言,这一过程备受煎熬。不过还是从中有所收益的(违心。。) 以下为完整代码:
|