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