純?cè)鷍s實(shí)現(xiàn)table表格的增刪
公司實(shí)習(xí)生問(wèn)我table的增刪操作,用jQuery很簡(jiǎn)單的實(shí)現(xiàn)了。又問(wèn)我不使用jQuery,只使用js如何實(shí)現(xiàn)。
面對(duì)這種情況,我的一貫做法是‘不理解,但是支持'。
jQuery用的多了,人也懶了,但還是用js實(shí)現(xiàn)了這一操作,覺得難點(diǎn)在于IE兼容。。。
只是想找代碼看看的可以跳過(guò)分析過(guò)程,文章底部附有完整代碼。
以下是coding過(guò)程:
HTML結(jié)構(gòu)代碼
一個(gè)基本的table結(jié)構(gòu),增加了一些簡(jiǎn)單的樣式,三個(gè)按鈕分別對(duì)應(yīng)創(chuàng)建、清空,和一個(gè)預(yù)留。
<!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>
構(gòu)造函數(shù)(偽構(gòu)造函數(shù))
考慮過(guò),創(chuàng)建一個(gè)隱藏的tr,基于此tr執(zhí)行創(chuàng)建操作。為了不破壞HTML整體結(jié)構(gòu),決定通過(guò)js生成tr對(duì)象并append到頁(yè)面中。
為了在頁(yè)面加載完成后,再執(zhí)行dom操作,所以將<script>放在代碼下端</body>之前。
基于table中的tbody進(jìn)行增刪操作,可以先聲明此全局變量
var vTbody = document.getElementById('main-table').getElementsByTagName('tbody')[0];
創(chuàng)建對(duì)象,可以使用document.createElement方法。
以面向?qū)ο蟮姆绞竭M(jìn)行編程,先寫構(gòu)造函數(shù)(其實(shí)并不是標(biāo)準(zhǔn)的構(gòu)造函數(shù)格式),從最內(nèi)部的元素開始。
td中可能會(huì)有text和button等表單元素,所以先創(chuàng)建一個(gè) input 的構(gòu)造函數(shù)function myInput(vId, vClass, vType, vValue, vParent){}
這里有一個(gè)兼容性問(wèn)題,就是IE內(nèi)核不支持setAttribute(class, value),需要使用setAttribute(className, value),所以為了解決兼容問(wèn)題,可以通過(guò)
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對(duì)象和tr對(duì)象的構(gòu)造函數(shù),大同小異,代碼如下
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()
構(gòu)造函數(shù)完成之后,完善createTr()方法。
預(yù)想的tr結(jié)構(gòu)為 序號(hào),文本框,操作按鈕。
依次創(chuàng)建相關(guān)對(duì)象。序號(hào)列需要?jiǎng)討B(tài)刷新,所以先設(shè)定class名稱,通過(guò)方法執(zhí)行排序操作。
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()
創(chuàng)建一個(gè)動(dòng)態(tài)排序方法reSequence() ,有一個(gè)兼容性問(wèn)題 innerText在火狐下無(wú)效果,所以使用innerHTML。代碼如下
function reSequence() {
var vObj = vTbody.getElementsByClassName('seq');
for (var i=0, len=vObj.length; i<len; i++) {
vObj[i].innerHTML = i+1;
}
}
有一個(gè)兼容性問(wèn)題,IE8及以下不支持getElementsByClassName()方法,網(wǎng)上找到了解決方案
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(){}
可惜沒有實(shí)現(xiàn)。
修改后的代碼為
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;
}
}
除了排序外,還需其他操作,所以我們創(chuàng)建一個(gè)init()方法,集中管理reSequence()這些方法,在createTr()方法的結(jié)尾調(diào)用init()方法。
清空行方法clearTrs()
移除/銷毀某個(gè)dom對(duì)象,首先想到的是remove()方法,不幸的是,存在IE瀏覽器兼容問(wèn)題,因此,采用了一個(gè)更簡(jiǎn)便的方式,對(duì)dom對(duì)象執(zhí)行innerHTML="",代碼如下
function clearTrs() {
vTbody.innerHTML = '';
}
IE8報(bào)錯(cuò),'未知的運(yùn)行錯(cuò)誤'。查了以下,因?yàn)閕e8的table.innerHTML是只讀屬性,妹的!再改:
function clearTrs() {
while(vTbody.rows.length >0) {
vTbody.deleteRow();
}
}
刪除行方法addBtnDelsListener()
接下來(lái),給DELETE按鈕綁定刪除當(dāng)前行的方法。
為了解決兼容性問(wèn)題,網(wǎng)上給出的方法是針對(duì)不同瀏覽器(IE、非IE)分別使用addEventListener、attachEvent方法,
我采用的是另一種解決方案:
obj.onclick = function(){};
匿名函數(shù)的方法體,吸取了上面clearTrs()方法的經(jīng)驗(yàn)教訓(xùn),直接采用deleteRow(index)方法。
有一點(diǎn)需要注意thisTr.rowIndex獲取的行數(shù),比當(dāng)前行要大2,因?yàn)閠head中還有兩行。所以當(dāng)前的索引數(shù)=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();
};
}
}
執(zhí)行完刪除操作后,通過(guò)reSequence()方法重新排序。
同時(shí)將addBtnDelsListener()方法加入到init()方法中。
復(fù)制行方法addBtnCpsListener()
再來(lái)看一下COPY按鈕,添加事件監(jiān)聽的方式同上。
如果innerHTML不是只讀的話,可以createElement一個(gè)tr元素 然后newTr.innerHTML=thisTr.innerHTML,
為了兼容性,必須做些改變。
其實(shí)可以將復(fù)制看做是新建,唯一的不同在于新建行的文本輸入框的內(nèi)容要等同于被復(fù)制行。
這就簡(jiǎn)單了。我可以先調(diào)用createTr()方法,再將最后一個(gè)元素lastChild中的文本框的value等于被復(fù)制行。
思路有了,代碼如下:
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;
}
}
}
優(yōu)化修改
進(jìn)行一些優(yōu)化修改工作:
var elements = new Array();
修改為:var elements = [];
原因:數(shù)組用[]更好
將addBtnDelsListener方法中的vBtnDels[i].onclick = function() {
修改為:vBtnDels[i].onclick = delTr;
外部新創(chuàng)建一個(gè)函數(shù)
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;
外部新創(chuàng)建一個(gè)函數(shù)
<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函數(shù)。
修改為:
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;
}
}
原因:?:預(yù)期返回值應(yīng)該是一個(gè)變量or函數(shù),而不應(yīng)該是一個(gè)表達(dá)式操作。
有一點(diǎn)需要注意:js最佳實(shí)現(xiàn)經(jīng)??吹揭褂?==替換==。但是本示例中的==null,如果替換成===null會(huì)在ie8一下版本中出現(xiàn)問(wèn)題。
完整代碼
至此,一個(gè)完全基于原生JavaScript,并且兼容至IE6的table增刪完成了。
還是想吐槽一下,如果不兼容IE10以下的版本,可以節(jié)省50%的代碼。如果使用jQuery,又可以節(jié)省50%的代碼。對(duì)于實(shí)用主義的我而言,這一過(guò)程備受煎熬。不過(guò)還是從中有所收益的(違心。。)
以下為完整代碼:
<!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>
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
- vue.js+Element實(shí)現(xiàn)表格里的增刪改查
- JS對(duì)HTML表格進(jìn)行增刪改操作
- JavaScript獲取表格(table)當(dāng)前行的值、刪除行、增加行
- Vue.js實(shí)現(xiàn)表格動(dòng)態(tài)增加刪除的方法(附源碼下載)
- AngularJS實(shí)現(xiàn)表格的增刪改查(僅限前端)
- javascript實(shí)現(xiàn)動(dòng)態(tài)增加刪除表格行(兼容IE/FF)
- js操作table元素實(shí)現(xiàn)表格行列新增、刪除技巧總結(jié)
- 使用Bootstrap + Vue.js實(shí)現(xiàn)表格的動(dòng)態(tài)展示、新增和刪除功能
- JavaScript實(shí)現(xiàn)動(dòng)態(tài)增刪表格的方法
- javascript實(shí)現(xiàn)表格信息增添與刪除
相關(guān)文章
用js模擬JQuery的show與hide動(dòng)畫函數(shù)代碼
用javascript實(shí)現(xiàn)的模擬jquery下的顯示與隱藏的動(dòng)畫效果,學(xué)習(xí)的朋友可以參考下。2010-09-09
超強(qiáng)推薦的js編程中的簡(jiǎn)潔寫法收集
超強(qiáng)推薦的js編程中的簡(jiǎn)潔寫法收集...2007-08-08
JavaScript ES6的新特性使用新方法定義Class
這篇文章主要介紹了JavaScript ES6的新特性使用新方法定義Class 的相關(guān)資料,需要的朋友可以參考下2016-06-06
javascript實(shí)現(xiàn)前端分頁(yè)功能
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)前端分頁(yè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
原生JS+HTML5實(shí)現(xiàn)的可調(diào)節(jié)寫字板功能示例
這篇文章主要介紹了原生JS+HTML5實(shí)現(xiàn)的可調(diào)節(jié)寫字板功能,涉及javascript結(jié)合HTML5屬性動(dòng)態(tài)操作頁(yè)面元素實(shí)現(xiàn)繪圖功能相關(guān)技巧,需要的朋友可以參考下2018-08-08
JS實(shí)現(xiàn)的自定義水平滾動(dòng)字體插件完整實(shí)例
這篇文章主要介紹了JS實(shí)現(xiàn)的自定義水平滾動(dòng)字體插件,以完整實(shí)例形式分析了javascript自定義水平滾動(dòng)字體插件的定義、原理與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06

