為了在CardSimulate項目中方便的顯示技能和效果列表,決定重構以前編寫的一段JavaScript代碼——att表格繪製庫,這段代碼的作用是將特定的JavaScript數據對象轉化為表格,支持精細的樣式設置和一些複雜報表功能並且提供了自由的擴展性。可以用較新的Chrome瀏覽器訪問https: ...
為了在CardSimulate項目中方便的顯示技能和效果列表,決定重構以前編寫的一段JavaScript代碼——att表格繪製庫,這段代碼的作用是將特定的JavaScript數據對象轉化為表格,支持精細的樣式設置和一些複雜報表功能並且提供了自由的擴展性。可以用較新的Chrome瀏覽器訪問https://ljzc002.github.io/Att/HTML/TEST/AttSample.html查看新版代碼的例子,舊版代碼的介紹見:https://www.cnblogs.com/ljzc002/p/5511510.html。
1、從表格類初始化表格對象
舊版的代碼直接將表格對象作為一個全局變數,新版代碼則定義了一個表格類,而每一個表格對象則是表格類的實例,這樣就可以方便的在一個頁面里添加多個表格對象,並有條理的管理多個表格的工作流程和屬性。表格對象的初始化代碼如下:
1 /** 2 * Created by Administrator on 2015/5/11. 3 */ 4 //動態畫表類,嘗試使用自包含結構 5 //2016/8/31在表格中加入更多的格式選擇 6 //2018/10/31重構att6框架為att7版本 7 Att7=function() 8 { 9 10 } 11 Att7.prototype.init=function(param)//只初始化對象的屬性,不實際繪製 12 { 13 try 14 { 15 this.base=param.base;//表格的容器對象 16 this.id=param.id;//表格的id 17 //this.left=param.left?param.left:0;//在容器對象內的左側距離->認為tab_data和div_table完全重合 18 //this.top=param.top?param.top:0;//上部距離 19 this.rowsp=param.rowsp?param.rowsp:50;//預設每頁顯示50條數據,輸入負值表示無限制 20 //this.page_current=param.page_current?param.page_current:0;//預設顯示數據集的第一頁,初始索引為0 21 this.isStripe=param.isStripe?param.isStripe:1;//這種三目運算不適用於布爾值!!!!預設奇偶行使用不同顏色 22 this.isThlocked=param.isThlocked?param.isThlocked:0;//預設不鎖定表頭 23 this.isCollocked=param.isCollocked?param.isCollocked:0;//預設不鎖定表列 24 this.showIndex=param.showIndex?param.showIndex:1;//預設在左側顯示行號 25 this.baseColor=param.baseColor?param.baseColor:"#ffffff";//預設背景色為白色,間隔色為背景色亮度降低十六分之一 26 this.stripeColor=param.stripeColor?param.stripeColor:"#eeeeee";//有時要求奇數行和偶數行使用不同的顏色 27 this.pickColor=param.pickColor?param.pickColor:"#97ceef";//選擇了某一行時要突出顯示這一行 28 this.div_temp1=document.createElement("div");//這幾個div用來對背景顏色進行比較,因為不同的瀏覽器對背景顏色的保存方式不同 29 this.div_temp1.style.backgroundColor=this.baseColor;//有的用小寫字母有的用大寫字母,有的用rgb+數字,所以這裡主動建立div 30 this.div_temp2=document.createElement("div");//在同樣的保存方式下對顏色進行比較 31 this.div_temp2.style.backgroundColor=this.stripeColor; 32 this.div_temp3=document.createElement("div"); 33 this.div_temp3.style.backgroundColor=this.pickColor; 34 this.str_indexwid=param.str_indexwid?param.str_indexwid:"100px";//索引列的寬度 35 this.num_toolhei=param.num_toolhei?param.num_toolhei:80;//表格上部的工具區的高度 36 //固有屬性,點擊某些單元格時可以打開的小視窗 37 this.html_onclick="<div class=\"div_inmod_lim\" style=\"width: 100%;height: 100%;margin: 0px;border: 1px solid;padding: 0px;" + 38 "float: left;line-height: 20px\"> " + 39 "<div class=\"div_inmod_head\" style=\"width: 100%;height: 20px;background-color: #E0ECFF;margin:0;border: 0;padding:0;border-bottom: 1px solid\">" + 40 " <span style=\"float: left;margin-left: 2px\">詳情</span>" + 41 "<BUTTON style=\'float:right;aposition:static; width: 14px;height: 14px; margin: 0;margin-top: 2px;margin-right:2px;padding: 0;" + 42 "background: url(../../ASSETS/IMAGE/close.png) no-repeat;border: 0px;vertical-align:top\' onclick=\"delete_div(\'div_bz\');\" type=submit></BUTTON> " + 43 "</div> " + 44 "<textarea class=\"div_inmod_lim_content\" style=\"width: 100%;height: 98px;overflow-x: hidden;margin:0;border: 0;padding:0\" contenteditable=\"false\"></textarea> </div>"; 45 this.html_onmouseover=//滑鼠移入時彈出的小文本提示框 46 "<div class=\"div_inmod_lim\" " + 47 "style=\"width: 100%;height: 100%;margin: 0px;border: 1px solid;padding: 0px;float: left;line-height: 20px\"> " + 48 "<textarea class=\"div_inmod_lim_content\" style=\"width: 100%;height: 100%;overflow-x: hidden;margin:0;border: 0;padding:0\" contenteditable=\"false\">" + 49 "</textarea> " + 50 "</div>"; 51 } 52 catch(e) 53 { 54 console.log("表格初始化異常!"+e); 55 return false; 56 } 57 return "ok"; 58 }
這裡設置了表格對象的各項屬性,第28到33行用不顯示的div解決了dom標簽顏色比較問題,第37到50行定義了兩個視窗小控制項以備後續調用。init方法的調用方式如下:
1 var table1=new Att7(); 2 var objp={ 3 base:"div_tab", 4 id:"table1", 5 //left:50, 6 //top:50, 7 rowsp:999, 8 isThlocked:1, 9 isCollocked:2,//不包括索引列?-》包括 10 baseColor:"#00ff00", 11 stripeColor:"#00aa00", 12 pickColor:"#97ceef" 13 } 14 if(table1.init(objp)=="ok") 15 {//下麵是數據顯示
2、表格容器的建立:
表格顯示時dom結構如下:
其中all_base是所有表格相關元素的總容器,div_tool是表格上面的工具區,裡面可以放置一些選擇篩選條件的控制項,div_tab是表格主體所在的區域,table1是根據數據生成的表格dom,三個div_mask是鎖定表頭或者鎖定表列時使用的遮罩層dom。
使用的樣式表文件如下:
1 /*專用於表格框架的樣式*/ 2 body{ margin: 0; padding: 0; border: 0; text-align: center; overflow: hidden;width: 100%; 3 height: 100%;position: fixed; font-family: verdana,arial,sans-serif; touch-action: none; 4 -ms-touch-action: none;font-size: 12px;min-width: 600px;} 5 #all_base{min-height: 576px;min-width: 1024px;height: 100%;width:100%;position: relative;overflow-x:auto;overflow-y: hidden;} 6 /*表格的屬性*/ 7 td input{ height: 100%; width: 100%; border:0; text-align: center; background-color: inherit;} 8 .div_tab{float: left;position: relative;width:4000px;overflow-x: hidden;overflow-y: scroll} 9 .div_tab td{ text-align: center; /*border: solid 1px #008000;*/ border-right:solid 1px #008000; border-bottom: solid 1px #008000; 10 line-height: 16px; font-size: 13px; height: 24px; padding: 1px; background-color: inherit; word-break: keep-all; 11 /*display: inline-block*/} 12 .div_tab th{ text-align: center; /*border: solid 1px #008000;*/ line-height: 16px; font-size: 13px; height: 36px; 13 padding: 1px; text-align: center; border-right: solid 1px #008000; border-bottom: solid 1px #008000; word-break: keep-all; 14 white-space:nowrap; overflow: hidden; text-overflow: ellipsis;/*display: inline-block*/} 15 .div_tab table{ float: left; width: auto; border-right-width:0px; border: solid 1px #008000; table-layout: fixed;} 16 .div_tab tr{ width: auto; vertical-align: middle; /*border: solid 1px #008000;*/ padding: 1px;} 17 td a{ cursor: pointer;} 18 td button{ cursor: pointer;} 19 .div_mask2{ display:block; left: 0px; top: 0px; /*filter: alpha(opacity=50); opacity: 0.50;*/ overflow: hidden;/*鎖定的表頭表列*/ 20 position: absolute; float: left; overflow-x: hidden} 21 table{ border-spacing:0;} 22 .div_mask2 td{ text-align: center; /*border: solid 1px #008000;*/ border-right:solid 1px #008000; border-bottom: solid 1px #008000; 23 line-height: 16px; font-size: 13px; height: 24px; padding: 1px; background-color: inherit; word-break: keep-all;} 24 .div_mask2 th{ text-align: center; /*border: solid 1px #008000;*/ line-height: 16px; font-size: 13px; height: 36px; 25 padding: 1px; text-align: center; border-right: solid 1px #008000; border-bottom: solid 1px #008000; word-break: keep-all; 26 white-space:nowrap; overflow: hidden; text-overflow: ellipsis;} 27 .div_mask2 table{ float: left; width: auto; border-right-width:0px; border: solid 1px #008000; table-layout: fixed; 28 position: absolute;} 29 .div_mask2 tr{ width: auto; vertical-align: middle; /*border: solid 1px #008000;*/ padding: 1px;} 30 .combo-panel li{ float:none;} 31 .btn_limlen{ /*float: left;*/ height: 20px; width: 20px; border: 1px solid; /*margin-top: 6px;*/ /*margin-left: 4px;*/ 32 background: url(../ASSETS/IMAGE/play.png) no-repeat; position: absolute; -moz-border-radius: 3px; /* Gecko browsers圓角 */ 33 -webkit-border-radius: 3px; /* Webkit browsers */ border-radius:3px; /* W3C syntax */ position: absolute; 34 top: 6px; right: 4px;}
遺憾的是,因為上述CSS的調試過程太長,以至於已經忘記了這樣設置的原因,如果您使用時出現莫名其妙的元素錯位,請自己調試。
3、啟動表格繪製
通過表格對象的draw方法啟動表格繪製
調用draw方法的方式如下:
1 if(table1.init(objp)=="ok") 2 { 3 var obj_datas=[ 4 "測試表格", 5 ["測試表頭","測試表頭","測試表頭","測試表頭","測試表頭","測試表頭","測試表頭","測試表頭"], 6 ["str" 7 ,"limit" 8 ,["switch",["value1","text1"],["value2","text2"]] 9 ,["input",["class1"],["height","10px"]] 10 ,["select","class2",[["value1","text1"],["value2","text2"],["value3","text3"]],"onChange()"] 11 ,["check","class3"] 12 ,["button","class4","按鈕","80px",["height","10px"]] 13 ,["a","class5",["height","10px"]] 14 ], 15 [100,200,300,400,500,600,700,800], 16 ["value1","value2value2value2value2value2value2value2value2value2value2","value1","value2","value1","value2","value1","value2"], 17 ["value1","value2","value1","value2","value1","value2","value1","value2"] 18 ,["value1","value2","value1","value2","value1","value2","value1","value2"] 19 ]; 20 table1.draw(obj_datas,0);//顯示數據obj_datas的第0行 21 requestAnimFrame(function(){table1.AdjustWidth();}); 22 }
其中obj_datas是一個自定義的數據對象,這個對象可能從後端程式發送過來也可能是在前臺組裝生成。requestAnimFrame是截取自谷歌WebGL工具庫的一個方法,用來“延時一會”,等待瀏覽器完成表格容器渲染後,再調整表格尺寸從而使表格佈局緊密。
延時代碼如下:
1 // Copyright 2010, Google Inc. 2 window.requestAnimFrame = (function() { 3 return window.requestAnimationFrame || 4 window.webkitRequestAnimationFrame || 5 window.mozRequestAnimationFrame || 6 window.oRequestAnimationFrame || 7 window.msRequestAnimationFrame || 8 function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { 9 window.setTimeout(callback, 1000/60); 10 }; 11 })();
4、表格繪製代碼的介紹:
a、首先做一些和表格翻頁有關的準備工作:
1 Att7.prototype.draw=function(data,page_current)//實際繪製dom元素 2 { 3 this.totalpages=0;//記錄下一共有多少頁 4 if(this.rowsp>0) 5 { 6 this.totalpages=Math.ceil((data.length-4)/this.rowsp); 7 } 8 if(this.totalpages==0) 9 { 10 this.totalpages=1; 11 } 12 //計算當前頁數 13 if(page_current<0) 14 { 15 alert("到達數據首頁!"); 16 this.page_current=0; 17 } 18 else if(page_current>=this.totalpages) 19 { 20 alert("到達數據末尾"); 21 this.page_current=this.totalpages-1; 22 } 23 else 24 { 25 this.page_current=page_current; 26 }
因為att將所有dom標簽的生成工作放在瀏覽器端,所以可以一次性將所有數據從後臺讀取到前端,由前端JavaScript程式進行分頁操作。(而傳統表格繪製工具多把dom標簽生成放在後臺程式中,為了降低後臺壓力,分頁操作多在資料庫層面進行)
翻頁方法代碼如下:
1 //翻頁處理 2 Att7.prototype.ChangePage=function(flag) 3 { 4 document.body.style.cursor='wait'; 5 switch(flag)//不同的翻頁動作對應不同的頁號處理 6 { 7 case "0": 8 { 9 this.page_current=0; 10 break; 11 } 12 case "+": 13 { 14 this.page_current++; 15 break; 16 } 17 case "-": 18 { 19 this.page_current--; 20 break; 21 } 22 case "9999": 23 { 24 this.page_current=9999; 25 break; 26 } 27 } 28 this.draw(this.data,this.page_current); 29 document.getElementById('t_page_span').innerHTML=this.totalpages; 30 try {//萬一沒有定義 31 AdjustColor(); 32 } 33 catch(e) 34 { 35 36 } 37 document.getElementById('c_page_span').innerHTML=this.page_current+1; 38 document.body.style.cursor='default'; 39 var _this=this; 40 try 41 { 42 requestAnimFrame(function () { 43 _this.AdjustWidth() 44 }); 45 } 46 catch(e) 47 { 48 49 } 50 }
根據ChangePage方法的不同參數,可以進行四種不同的翻頁操作,您可以再需要的地方建立四個按鈕來對應這些操作,而翻頁操作實際上只是改變了參數的draw方法。t_page_span和c_page_span是兩個span標簽,用來顯示總頁數和當前頁數。AdjustColor是一個可選的方法,在繪製表格後遍歷單元格,根據需求改變符合某種條件的單元格的顏色。(這裡並未使用)
b、在開始繪製之前清理以前可能繪製過的id相同的表格:
1 //接著上面的翻頁準備 2 this.data=data;//表格的數據集 3 var tab_data;//table標簽 4 var tab_colmask;//列鎖定遮罩標簽 5 if (document.getElementById(this.id))//如果已有該表 6 {//清理已有的dom 7 tab_data= document.getElementById(this.id); 8 var parent = tab_data.parentNode; 9 parent.removeChild(tab_data); 10 if(document.getElementById("div_thmask"))//刪除鎖定表頭的遮罩層 11 { 12 var div =document.getElementById("div_thmask");//看來這樣的設定還不能支持一個頁面中同時存在多個鎖定表頭表格 13 div.parentNode.removeChild(div); 14 } 15 if(document.getElementById("tab_mask2"))//刪除鎖定表列的遮罩層 16 { 17 var tab =document.getElementById("tab_mask2"); 18 tab.parentNode.removeChild(tab); 19 } 20 if(document.getElementById("div_thmask3"))// 21 { 22 var tab =document.getElementById("div_thmask3"); 23 tab.parentNode.removeChild(tab); 24 } 25 } 26 tab_data = document.createElement("table");//重新建立table標簽 27 tab_data.id = this.id; 28 tab_data.cellPadding = "0"; 29 tab_data.cellSpacing = "0"; 30 tab_data.style.position = "absolute"; 31 //tab_data.style.top = this.top + "px"; 32 //tab_data.style.left = this.left + "px"; 33 var div_table;//包含表格的容器元素 34 35 var obj=this.base;//這個屬性可能是id字元串也可能是對象本身 36 if((typeof obj)=="string"||(typeof obj)=="String") 37 { 38 div_table = document.getElementById(obj); 39 } 40 else 41 { 42 div_table=obj; 43 } 44 div_table.innerHTML=""; 45 div_table.appendChild(tab_data);//將table標簽放入容器里 46 this.div_table=div_table; 47 tab_data = document.getElementById(this.id);
c、表格表頭的繪製與遮罩原理
在一個簡單的表格裡繪製表頭並不複雜:
1 var tr1 = document.createElement("tr");//填寫表頭(接著清理代碼) 2 if(this.showIndex==1)//如果顯示索引列 3 { 4 this.InsertaTHStr(tr1, "第"+(this.page_current+1) + "頁",this.str_indexwid);//IE8中缺少參數會報錯 5 } 6 for (var k = 0; k < data[1].length; k++) 7 { 8 this.InsertaTHStr(tr1, data[1][k],(data[3][k]+"px")); 9 } 10 tab_data.appendChild(tr1);//將tr放入table 11 tr1.style.backgroundColor=this.baseColor;
如果選擇顯示索引列,則在表頭的最左側多插入一個th,InsertaTHStr方法用來向指定tr中插入th,參數分別是tr對象、列名、列寬,這裡的data也就是之前構造的數據集。
InsertaTHStr代碼如下:
1 //一些工具方法 2 /** 3 * 向一個表行中添加字元型表頭元素 4 * @param tr 表行ID 5 * @param str 添加字元 6 * @param wid 列寬(字元型px) 7 * @constructor 8 */ 9 Att7.prototype.InsertaTHStr=function(tr,str,wid) 10 { 11 var th=document.createElement("th"); 12 th.style.width=wid?wid:"200px"; 13 if(str==null) 14 { 15 str=""; 16 } 17 th.appendChild(document.createTextNode(str)); 18 tr.appendChild(th); 19 }
然而當需要鎖定表頭或者鎖定表列時,事情變得複雜,接著繪製表頭的代碼:
1 this.arr_lock=[];//all_base左右滑動時需要調整位置的元素 2 this.arr_locky=[]; 3 if(this.isThlocked==1)//繪製鎖定表頭的遮罩層,它的內容和原表格的表頭是一樣的 4 { 5 var div_thmask=document.createElement("div"); 6 div_thmask.className="div_mask2"; 7 div_thmask.id="div_thmask"; 8 div_thmask.style.zIndex="200"; 9 var div_parent=div_table.parentNode; 10 this.div_parent=div_parent; 11 div_thmask.style.top=(compPos2(div_table).top-parseInt(div_table.style.height.split("p")[0]))+this.top+"px";//定位添加的遮罩層 12 div_thmask.style.left=compPos2(div_table).left+this.left+"px"; 13 div_thmask.style.width="6000px";//遮罩的最大寬度 14 div_thmask.style.height="42px"; 15 div_thmask.style.top=this.num_toolhei+"px"; 16 //div_thmask.getElementsByTagName("table")[0].style.backgroundColor=this.baseColor; 17 18 var tab_thmask= document.createElement("table"); 19 var tr_thmask=document.createElement("tr"); 20 if(this.showIndex==1)//如果不禁止索引列 21 { 22 this.InsertaTHStr(tr_thmask, "第" + (this.page_current + 1) + "頁", this.str_indexwid);//IE8中缺少參數會報錯 23 } 24 for (var k = 0; k < data[1].length; k++) 25 { 26 this.InsertaTHStr(tr_thmask, data[1][k],(data[3][k]+"px")); 27 } 28 tab_thmask.appendChild(tr_thmask); 29 tab_thmask.style.backgroundColor=this.baseColor; 30 div_thmask.appendChild(tab_thmask); 31 div_parent.appendChild(div_thmask); 32 } 33 if(this.isCollocked>0)//繪製鎖定表列的遮罩層,估計不需要外包裝的div,可以和data_table共用div_table(考慮到層數決定這樣做) 34 { 35 this.arr_lock.push(["tab_mask2",1,0]);//第一個參數是要鎖定的標簽的id,第二個是是否鎖定,第三個是標簽的初始水平偏移量 36 this.arr_lock.push(["div_bz",0,0]); 37 tab_colmask= document.createElement("table"); 38 tab_colmask.cellPadding = "0"; 39 tab_colmask.cellSpacing = "0"; 40 tab_colmask.style.position = "absolute"; 41 tab_colmask.className="div_mask2"; 42 tab_colmask.id="tab_mask2"; 43 tab_colmask.style.zIndex="150"; 44 tab_colmask.style.top="0px"; 45 tab_colmask.style.backgroundColor=this.baseColor 46 var tr_mask= document.createElement("tr");//創造一個占位用的表頭行 47 if(this.showIndex==1)//如果不禁止索引列 48 { 49 this.InsertaTHStr(tr_mask, "第" + (this.page_current + 1) + "頁", this.str_indexwid); 50 } 51 for (var k = 0; k < this.isCollocked-1; k++) 52 { 53 this.InsertaTHStr(tr_mask, data[1][k],(data[3][k]+"px")); 54 } 55 tab_colmask.appendChild(tr_mask); 56 } 57 //如果同時鎖定了表頭和左側的表列 58 if((this.isThlocked==1)&&(this.isCollocked>0)) 59 { 60 this.arr_lock.push(["div_thmask3",1,0]); 61 var div_thmask=document.createElement("div"); 62 div_thmask.className="div_mask2"; 63 div_thmask.id="div_thmask3"; 64 div_thmask.style.zIndex="250"; 65 var div_parent=div_table.parentNode;