首頁 > web前端 > js教程 > 主體

純原生js實作table表格的增刪

高洛峰
發布: 2017-01-07 10:12:37
原創
1231 人瀏覽過

公司實習生問我table的增刪操作,用jQuery很簡單的實作了。又問我不使用jQuery,只使用js如何實作。

面對這種情況,我的一貫做法是『不理解,但是支持』。

jQuery用的多了,人也懶了,但還是用js實現了這一操作,覺得困難在於IE相容。 。 。

只是想找程式碼看看的可以跳過分析過程,文章底部附有完整程式碼。

以下是coding流程:

HTML結構程式碼

一個基本的table結構,增加了一些簡單的樣式,三個按鈕分別對應建立、清空,和一個預留。

<!DOCTYPE HTML> 
<html> 
 <head> 
  <title>table</title> 
  <meta charset=&#39;utf-8&#39; /> 
  <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>
登入後複製

建構子(偽建構子)

考慮過,建立一個隱藏的tr,基於此tr執行建立操作。為了不破壞HTML整體結構,決定透過js產生tr物件並append到頁面中。

為了在頁面載入完成後,再執行dom操作,所以將<script>放在程式碼下端</body>之前。 </p><p>基於table中的tbody進行增刪操作,可以先宣告此全域變數</p><p>var vTbody = document.getElementById('main-table').getElementsByTagName('tbody')[0]; </p><p>建立物件,可以使用建立物件,可以使用document.createElement方法。 </p><p>以物件導向的方式進行編程,先寫構造函數(其實不是標準的構造函數格式),從最內部的元素開始。 </p><p>td中可能會有text和button等表單元素,所以先建立一個input 的建構子function myInput(vId, vClass, vType, vValue, vParent){}</p><p>這裡有一個相容性問題,就是IE核心不支援setAttribute(class, value),需要使用setAttribute(className, value),所以為了解決相容問題,可以透過</p><p>setAttribute(class, value) for FF、Chrome..</p><p>setAttribute(className, value) for IE</p>setAttribute(className, value) for IE<p>setAttribute(className, value) for IE </p>這裡採用的是另一種方式.className,程式碼如下:<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function myInput(vId, vClass, vType, vValue, vParent) { var vInput = document.createElement(&amp;#39;input&amp;#39;); if(vId) { vInput.setAttribute(&amp;#39;id&amp;#39;, vId); } vInput.setAttribute(&amp;#39;type&amp;#39;, vType); vInput.setAttribute(&amp;#39;value&amp;#39;, vValue); vInput.className = vClass; if(vParent) { vParent.appendChild(vInput); } }</pre><div class="contentsignin">登入後複製</div></div></p>接著是td物件和tr物件的建構函數,大同小異,程式碼如下<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function myTd(vId, vClass, vChild, vParent) { var vTd = document.createElement(&amp;#39;td&amp;#39;); if(vId){ vTd.setAttribute(&amp;#39;id&amp;#39;, 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(&amp;#39;tr&amp;#39;); if(vId){ vTr.setAttribute(&amp;#39;id&amp;#39;, vId); } vTr.className = vClass; if(vChild) { vTr.appendChild(vChild); } if(vParent) { vParent.appendChild(vTr); } return vTr; }</pre><div class="contentsignin">登入後複製</div></div></p>新建行方法createTr()<p></p>建構函數完成之後,完善createTr()方法。 <p></p>預想的tr結構為 序號,文字框,操作按鈕。 <p></p>依序建立相關物件。序號列需要動態刷新,所以先設定class名稱,透過方法執行排序操作。 <p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function createTr() { var vTr = new myTr(null, null, null, vTbody); //序列td var vTdSeq = new myTd(null, &amp;#39;seq&amp;#39;, null, vTr); //文本框td var vTdText = new myTd(null, null, null, vTr); var vInputText = new myInput(null, &amp;#39;td-inp-txt&amp;#39;, &amp;#39;text&amp;#39;, &amp;#39;&amp;#39;, vTdText); //操作按钮td var vTdBtn = new myTd(null, null, null, vTr); var vInputBtnCp = new myInput(null, &amp;#39;td-inp-btn-cp&amp;#39;, &amp;#39;button&amp;#39;, &amp;#39;COPY&amp;#39;, vTdBtn); var vInputBtnDel = new myInput(null, &amp;#39;td-inp-btn-del&amp;#39;, &amp;#39;button&amp;#39;, &amp;#39;DELETE&amp;#39;, vTdBtn); }</pre><div class="contentsignin">登入後複製</div></div></p>排序方法reSequence()<p></p>建立一個動態排序方法reSequence() ,有一個相容性問題 innerText在火狐下無效果,所以使用innerHTML。程式碼如下<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbar:false">function reSequence() { var vObj = vTbody.getElementsByClassName(&amp;#39;seq&amp;#39;); for (var i=0, len=vObj.length; i&lt;len; i++) { vObj[i].innerHTML = i+1; } }</pre><div class="contentsignin">登入後複製</div></div></p>有一個相容性問題,IE8及以下不支援getElementsByClassName()方法,網路上找到了解決方案<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">if(!document.getElementsByClassName){ document.getElementsByClassName = function(className, element){ var children = (element || document).getElementsByTagName(&amp;#39;*&amp;#39;); var elements = new Array(); for (var i=0; i&lt;children.length; i++){ var child = children[i]; var classNames = child.className.split(&amp;#39; &amp;#39;); for (var j=0; j&lt;classNames.length; j++){ if (classNames[j] == className){ elements.push(child); break; } } } return elements; }; }</pre><div class="contentsignin">登入後複製</div></div></p>試圖在Object或是HTMLTableSectionElement的原型上增加此方法,如同一份<p></p>HTMLTableSectionElement.Btotype. = function(){} <p></p>可惜沒有實現。 <p></p>修改後的程式碼為<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function reSequence() { var vObj = vTbody.getElementsByClassName == null?document.getElementsByClassName(&amp;#39;seq&amp;#39;, vTbody):vTbody.getElementsByClassName(&amp;#39;seq&amp;#39;); for (var i=0, len=vObj.length; i&lt;len; i++) { vObj[i].innerHTML = i+1; } }</pre><div class="contentsignin">登入後複製</div></div></p>除了排序外,還需其他操作,所以我們建立一個init()方法,集中管理reSequence()這些方法,在createTr()方法的結尾呼叫init()方法。 <p></p>清空行方法clearTrs()<p></p>移除/銷毀某個dom對象,首先想到的是remove()方法,不幸的是,存在IE瀏覽器兼容問題,因此,採用了一個更簡便的方式,對dom物件執行innerHTML="",程式碼如下<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function clearTrs() { vTbody.innerHTML = &amp;#39;&amp;#39;; }</pre><div class="contentsignin">登入後複製</div></div></p>IE8報錯,'未知的運行錯誤'。查了以下,因為ie8的table.innerHTML是唯讀屬性,妹的!再改:<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function clearTrs() { while(vTbody.rows.length &gt;0) { vTbody.deleteRow(); } }</pre><div class="contentsignin">登入後複製</div></div></p>刪除行方法addBtnDelsListener()<p></p>接下來,給DELETE按鈕綁定刪除當前行的方法。 <p></p>為了解決相容性問題,網路上給出的方法是針對不同瀏覽器(IE、非IE)分別使用addEventListener、attachEvent方法,<p></p>我採用的是另一個解決方案:<p></p>obj.onclick = function (){};<p></p>匿名函數的方法體,吸取了上面clearTrs()方法的經驗教訓,直接採用deleteRow(index)方法。 <p></p>有一點要注意thisTr.rowIndex取得的行數,比目前行要大2,因為thead還有兩行。所以目前的索引數=thisTr.rowIndex-vThead.rows.length<p></p>程式碼如下:<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function addBtnDelsListener() { var vBtnDels = vTbody.getElementsByClassName == null?document.getElementsByClassName(&amp;#39;td-inp-btn-del&amp;#39;, vTbody):vTbody.getElementsByClassName(&amp;#39;td-inp-btn-del&amp;#39;); for (var i=0, len=vBtnDels.length; i&lt;len; i++) { vBtnDels[i].onclick = function() { var vTr = this.parentElement.parentElement; vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName(&amp;#39;thead&amp;#39;)[0].rows.length); reSequence(); }; } }</pre><div class="contentsignin">登入後複製</div></div></p>執行完刪除操作後,透過reSequence()方法重新排序。 <p></p>同時將addBtnDelsListener()方法加入init()方法中。 <p></p>複製行方法addBtnCpsListener()<p></p>再來看一下COPY按鈕,加入事件監聽的方式同上。 <p></p>如果innerHTML不是唯讀的話,可以createElement一個tr元素 然後newTr.innerHTML=thisTr.innerHTML,<p></p>為了相容性,必須做些改變。 <p></p>其實可以將複製看做是新建,唯一的不同在於新建行的文字輸入框的內容要等同於被複製行。 <p></p>這就簡單了。我可以先呼叫createTr()方法,再將最後一個元素lastChild中的文字方塊的value等於被複製行。 <p></p>思路有了,程式碼如下:<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function addBtnCpsListener() { var vBtnCps = vTbody.getElementsByClassName == null?document.getElementsByClassName(&amp;#39;td-inp-btn-cp&amp;#39;, vTbody):vTbody.getElementsByClassName(&amp;#39;td-inp-btn-cp&amp;#39;); for (var i=0, len=vBtnCps.length; i&lt;len; i++) { vBtnCps[i].onclick = function() { createTr(); var vNewTr = vTbody.lastChild; var vTr = this.parentElement.parentElement; vNewTr.getElementsByClassName == null?document.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;, vNewTr)[0].value = document.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;, vTr)[0].value:vNewTr.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;)[0].value = vTr.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;)[0].value; } } }</pre><div class="contentsignin">登入後複製</div></div></p>最佳化修改<p></p>進行一些最佳化修改工作:<p></p>var elements = new Array();<p></p>修改為:var elements = [];<p>組]更好</p><p>將addBtnDelsListener方法中的vBtnDels[i].onclick = function() {</p><p>修改为:vBtnDels[i].onclick = delTr;</p><p>外部新创建一个函数</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function delTr() { var vTr = this.parentElement.parentElement; vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName(&amp;#39;thead&amp;#39;)[0].rows.length); reSequence(); }</pre><div class="contentsignin">登入後複製</div></div><p>原因:Don&#39;t make functions within a loop.</p><p>同理,将addBtnCpsListener中的vBtnCps[i].onclick = function() {</p><p>修改为:vBtnCps[i].onclick = copyTr;</p><p>外部新创建一个函数</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">&lt;pre code_snippet_id=&quot;139791&quot; snippet_file_name=&quot;blog_20140103_15_6784659&quot; name=&quot;code&quot; class=&quot;javascript&quot;&gt; function copyTr() { createTr(); var vNewTr = vTbody.lastChild; var vTr = this.parentElement.parentElement; vNewTr.getElementsByClassName === null? document.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;, vNewTr)[0].value = document.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;, vTr)[0].value: vNewTr.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;)[0].value = vTr.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;)[0].value; }</pre> <pre class="brush:php;toolbar:false"></pre> <pre class="brush:php;toolbar:false"></pre></pre><div class="contentsignin">登入後複製</div></div><p>将copyTr()方法中的?:格式修改为if else函数。</p><p>修改为:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false">function copyTr() { createTr(); var vNewTr = vTbody.lastChild; var vTr = this.parentElement.parentElement; if(vNewTr.getElementsByClassName) { vNewTr.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;)[0].value = vTr.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;)[0].value; } else { document.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;, vNewTr)[0].value = document.getElementsByClassName(&amp;#39;td-inp-txt&amp;#39;, vTr)[0].value; } }</pre><div class="contentsignin">登入後複製</div></div><p>原因:?:预期返回值应该是一个变量or函数,而不应该是一个表达式操作。</p><p>有一点需要注意:js最佳实现经常看到要使用===替换==。但是本示例中的==null,如果替换成===null会在ie8一下版本中出现问题。</p><p>完整代码</p><p>至此,一个完全基于原生JavaScript,并且兼容至IE6的table增删完成了。</p><p>还是想吐槽一下,如果不兼容IE10以下的版本,可以节省50%的代码。如果使用jQuery,又可以节省50%的代码。对于实用主义的我而言,这一过程备受煎熬。不过还是从中有所收益的(违心。。)</p><p>以下为完整代码:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbar:false"><!DOCTYPE HTML> <html> <head> <title>table</title> <meta charset=&#39;utf-8&#39; /> <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(&#39;*&#39;); var elements = []; for (var i=0; i<children.length; i++){ var child = children[i]; var classNames = child.className.split(&#39; &#39;); for (var j=0; j<classNames.length; j++){ if (classNames[j] == className){ elements.push(child); break; } } } return elements; }; } var vTbody = document.getElementById(&#39;main-table&#39;).getElementsByTagName(&#39;tbody&#39;)[0]; function myInput(vId, vClass, vType, vValue, vParent) { var vInput = document.createElement(&#39;input&#39;); if(vId) { vInput.setAttribute(&#39;id&#39;, vId); } vInput.setAttribute(&#39;type&#39;, vType); vInput.setAttribute(&#39;value&#39;, vValue); vInput.className = vClass; if(vParent) { vParent.appendChild(vInput); } return vInput; } function myTd(vId, vClass, vChild, vParent) { var vTd = document.createElement(&#39;td&#39;); if(vId){ vTd.setAttribute(&#39;id&#39;, 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(&#39;tr&#39;); if(vId){ vTr.setAttribute(&#39;id&#39;, 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, &#39;seq&#39;, null, vTr); //文本框td var vTdText = new myTd(null, null, null, vTr); var vInputText = new myInput(null, &#39;td-inp-txt&#39;, &#39;text&#39;, &#39;&#39;, vTdText); //操作按钮td var vTdBtn = new myTd(null, null, null, vTr); var vInputBtnCp = new myInput(null, &#39;td-inp-btn-cp&#39;, &#39;button&#39;, &#39;COPY&#39;, vTdBtn); var vInputBtnDel = new myInput(null, &#39;td-inp-btn-del&#39;, &#39;button&#39;, &#39;DELETE&#39;, 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(&#39;seq&#39;, vTbody): vTbody.getElementsByClassName(&#39;seq&#39;); for (var i=0, len=vObj.length; i<len; i++) { vObj[i].innerHTML = i+1; } } function addBtnDelsListener() { var vBtnDels = vTbody.getElementsByClassName == null? document.getElementsByClassName(&#39;td-inp-btn-del&#39;, vTbody): vTbody.getElementsByClassName(&#39;td-inp-btn-del&#39;); 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(&amp;#39;thead&amp;#39;)[0].rows.length); reSequence(); } function addBtnCpsListener() { var vBtnCps = vTbody.getElementsByClassNamenull == null? document.getElementsByClassName(&#39;td-inp-btn-cp&#39;, vTbody): vTbody.getElementsByClassName(&#39;td-inp-btn-cp&#39;); 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(&#39;td-inp-txt&#39;)[0].value = vTr.getElementsByClassName(&#39;td-inp-txt&#39;)[0].value; } else { document.getElementsByClassName(&#39;td-inp-txt&#39;, vNewTr)[0].value = document.getElementsByClassName(&#39;td-inp-txt&#39;, vTr)[0].value; } } </script>

登入後複製

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持PHP中文网!

更多纯原生js实现table表格的增删相关文章请关注PHP中文网!


相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門推薦
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!