重構一段基於原生JavaScript的表格繪製代碼

爲了在CardSimulate項目中方便的顯示技能和效果列表,決定重構之前編寫的一段JavaScript代碼——att表格繪製庫,這段代碼的做用是將特定的JavaScript數據對象轉化爲表格,支持精細的樣式設置和一些複雜報表功能而且提供了自由的擴展性。能夠用較新的Chrome瀏覽器訪問https://ljzc002.github.io/Att/HTML/TEST/AttSample.html查看新版代碼的例子,舊版代碼的介紹見:https://www.cnblogs.com/ljzc002/p/5511510.html。css

一、從表格類初始化表格對象html

舊版的代碼直接將表格對象做爲一個全局變量,新版代碼則定義了一個表格類,而每個表格對象則是表格類的實例,這樣就能夠方便的在一個頁面裏添加多個表格對象,並有條理的管理多個表格的工做流程和屬性。表格對象的初始化代碼以下:前端

 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方法的調用方式以下:git

 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     {//下面是數據顯示

二、表格容器的創建:github

表格顯示時dom結構以下:web

其中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的調試過程太長,以致於已經忘記了這樣設置的緣由,若是您使用時出現莫名其妙的元素錯位,請本身調試。瀏覽器

三、啓動表格繪製app

經過表格對象的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 })();

四、表格繪製代碼的介紹:

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;
66         div_thmask.style.top=(compPos2(div_table).top-parseInt(div_table.style.height.split("p")))+"px";//定位添加的遮罩層
67         div_thmask.style.left=compPos2(div_table).left+"px";
68         div_thmask.style.width="4000px";
69         div_thmask.style.height="42px";
70         div_thmask.style.top=this.num_toolhei+"px";
71 
72         var tab_thmask= document.createElement("table");
73         tab_thmask.style.backgroundColor=this.baseColor;
74         var tr_thmask=document.createElement("tr");
75         if(this.showIndex==1)//若是不由止索引列
76         {
77             this.InsertaTHStr(tr_thmask, "第" + (this.page_current + 1) + "頁", this.str_indexwid);//IE8中缺乏參數會報錯
78         }
79         for (var k = 0; k < this.isCollocked-1; k++)
80         {
81             this.InsertaTHStr(tr_thmask, data[1][k],(data[3][k]+"px"));
82         }
83         tab_thmask.appendChild(tr_thmask);
84         div_thmask.appendChild(tab_thmask);
85         div_parent.appendChild(div_thmask);
86     }

實現表頭表列鎖定的思路是這樣的:首先all_base的大小固定爲all_base的容器的大小(在這裏等於窗口大小),而後把div_table設置的足夠寬(默認4000px),而高度則設爲all_base高度減div_tools高度的有限值,這樣當table的行數較多且div_table得到焦點時便可用鼠標滾輪控制div_table的內容的上下滾動,而由於div_table的寬度超過all_base,div_table的上下滑動條被隱藏起來。

在div_table的內容上下滾動時,由於div_thmask和div_thmask3在div_table外相對於all_base定位,因此不會受div_table滾動的影響,再將z-index設高一些,看起來就是表格內容滾動而表頭鎖定不變。

至於表列鎖定,首先禁用all_base的上下滑動,只保留左右滑動,由於div_table比all_base寬,因此all_base的左右滑動條一直存在,監聽all_base滑動條的滑動事件,在每次滑動時調整div_mask2的水平位置,便可達到看起來鎖定了表列的效果。

在同時鎖定了表頭和表列時,div_thmask3位於這幾個遮罩的最上層,表現兩者共同起做用的效果。

all_base滑動的響應方法以下:

 1 Att7.prototype.ScrollLock=function()//拖動滑動條時,彈出層隨拖動一同移動
 2 {
 3     var mask2left=0;
 4     var mask2top=0;
 5     var scrollleft=document.getElementById("all_base").scrollLeft;//scrollLeft指滑動條向右滑動的距離
 6     var scrolltop=document.getElementById("all_base").scrollTop;
 7     var arr_lock=this.arr_lock;
 8     var arr_locky=this.arr_locky;
 9     var leng=arr_lock.length;
10     for(var i=0;i<leng;i++)
11     {
12         if(arr_lock[i][1]==1)
13         {
14             //$("#"+arr_lock[i][0]).css("left",mask2left+scrollleft+arr_lock[i][2]+"px");
15             document.getElementById(arr_lock[i][0]).style.left=mask2left+scrollleft+arr_lock[i][2]+"px";
16         }
17     }
18     var leng2=arr_locky.length;
19     for(var i=0;i<leng2;i++)
20     {
21         if(arr_locky[i][1]==1)
22         {
23             //$("#"+arr_locky[i][0]).css("top",mask2top+scrolltop+arr_locky[i][2]+"px");
24             document.getElementById(arr_locky[i][0]).style.top=mask2top+scrolltop+arr_locky[i][2]+"px";
25         }
26     }
27 }

在實際使用中發現,雖然鎖定遮罩裏的內容和原表格裏的內容相同,但實際渲染時總會出現尺寸誤差,因此在完成渲染後執行AdjustWidth方法從新調整遮罩的寬度:

 1 //不斷修正讓遮罩層的寬高和底層一致
 2 Att7.prototype.AdjustWidth=function()
 3 {
 4     if(document.getElementById("div_thmask"))
 5     {
 6         var ths_mask = document.getElementById("div_thmask").getElementsByTagName("th");
 7         var ths = document.getElementById(this.id).getElementsByTagName("th");
 8         if (ths[0].offsetWidth) {//有寬度說明瀏覽器已經完成了渲染操做
 9             this.div_table.style.height=this.div_parent.offsetHeight-this.num_toolhei-12+"px";//調整div_table高度
10             var leng = ths.length;
11             for (var i = 0; i < leng; i++) {
12                 try {
13                     ths_mask[i].style.width = (ths[i].offsetWidth - 3) + "px";
14                 }
15                 catch (e) {
16                     //i--;
17                     continue;
18                 }
19             }
20             if (document.getElementById("div_thmask3")) {
21                 var div_thmask3 = document.getElementById("div_thmask3").getElementsByTagName("th");
22                 var leng2 = div_thmask3.length;
23                 for (var i = 0; i < leng2; i++) {
24                     div_thmask3[i].style.width = (ths[i].offsetWidth - 3) + "px";
25                 }
26             }
27             if (document.getElementById("tab_mask2"))
28             {
29                 var trs_mask = document.getElementById("tab_mask2").getElementsByTagName("tr");
30                 var trs = document.getElementById(this.id).getElementsByTagName("tr");
31                 var leng3 = trs.length;
32                 for (var i = 1; i < leng3; i++)
33                 {
34                     trs_mask[i].style.height =(trs[i].offsetHeight)+"px";
35                 }
36             }
37         }
38         else {//若是尚未完成渲染,則再延時調用一次
39             var _this=this;
40             requestAnimFrame(function () {
41                 _this.AdjustWidth()//須要注意的是延時操做或者事件觸發時,原來的this對象已經隨着時間的推移被釋放掉了,因此用_this保持這個對象
42             });
43         }
44     }
45 }

若是您想爲表格添加動態調整列寬功能,能夠在列寬變化後調用這個方法;或者若是您想在瀏覽器尺寸變化後保持div_table和all_bas的緊密貼合也能夠將這個方法設爲resize事件的響應。

d、繪製最簡單的表格內容:

//接着上面的表頭繪製
if (this.rowsp > 0)//默認必需要分頁,數據集的第一行是表名、第二行是列名、第三行是列設定、第四行是列寬、第五行開始是數據
    {
        var rows=this.rowsp;//每一頁多少行
        var pages=this.page_current;//當前頁
        var collock=this.isCollocked;//鎖定幾個表列
        var count=0;//標記通過了幾個沒有數據源的列,存在按鈕等不填寫源數據的列時,data[2]會比data[l]長,爲了讓後面的類型和數據對應上,應該用m減去count!
        var count_none=0;//標記通過了幾個使用數據源但不顯示的列,
        for (var l = 4 + pages * rows; l < data.length && (l - pages * rows) < rows + 4; l++)
        {//遍歷當前頁中的每一條數據
            //dataObj2.push(data[l]);
            count=0;//繪製每一行時都把標記數設爲0,其後每檢測到一個標記就+1,data[l][m+count]從數據源取數
            count_none=0;
            var tr2 = document.createElement("tr");//填寫一個錶行
            var tr_mask = document.createElement("tr");//準備給遮罩層用
            if (l % 2 == 0&&this.isStripe==1)//偶數的數據行顯示爲間隔色
            {
                tr2.style.backgroundColor = this.stripeColor;
                tr_mask.style.backgroundColor = this.stripeColor;
            }
            else
            {
                tr2.style.backgroundColor = this.baseColor;
                tr_mask.style.backgroundColor = this.baseColor;
            }
            if(this.showIndex==1)//若是不由止索引列
            {
                this.InsertaTDPick(tr2, l - 3 + "");//這個是序號
                this.InsertaTDPick2(tr_mask, l - 3 + "", this.id);//遮罩層的序號
            }

            for (var m = 0; m < data[2].length; m++)//一行中的一個單元格,這裏可能有多種變化,在length範圍外的數據列不會被考慮
            {//根據數據源的第三個元素中存儲的DOM信息,爲數據的每一列設置不一樣的控件類型!!!!
                try
                {
                    if (data[2][m] == "str") //簡單的字符類型,要限制下寬度!
                    {
                        this.InsertaTDStr(tr2, data[l][m - count],(data[3][m-count_none]+"px"));
                        if(this.isCollocked>0&&(m+1)<this.isCollocked)
                        {
                            this.InsertaTDStr(tr_mask, data[l][m - count],(data[3][m-count_none]+"px"));
                        }
                    }

在實際使用中發現每一行數據集的元素數和表格每一行的列數並不老是能一一對應,有時表格的列數比數據集元素多,好比不包含數據集的控件,有時表格寬度比數據集短,好比某一列數據須要設定爲「不可見」,爲此設置了count和count_none兩個計數器對錶格和數據集的索引進行調整。

接下來設置每個數據tr的顏色,並在須要時向tr推入顯示行號的索引列單元格。

而後遍歷數據集這一行的每一個數據,根據設置的單元格類型,向tr中推入單元格,對於最簡單的str型單元格使用InsertaTDStr方法向tr中添加,其代碼以下:

 1 /**
 2  * 向一個錶行中添加字符型單元格元素
 3  * @param tr 錶行ID
 4  * @param str 添加字符
 5  * @param wid 列寬
 6  * @constructor
 7  */
 8 Att7.prototype.InsertaTDStr=function(tr,str,wid)
 9 {
10     var td=document.createElement("td");
11     td.style.width=wid?wid:"200px";
12     if(str==null)
13     {
14         str="";
15     }
16     td.appendChild(document.createTextNode(str));
17     tr.appendChild(td);
18 }

接着,若是有鎖定表列,則也向表列鎖定遮罩裏推入這個td。

e、前面的代碼中還出現了InsertaTDPick和InsertaTDPick2方法,它們的做用是經過點擊原表格或鎖定表列遮罩上的行號突出顯示某行數據:

 1 //一個能夠被選中的單元格,選中後改變單元格所在錶行的顏色以突出顯示
 2 Att7.prototype.InsertaTDPick=function (tr,str)
 3 {
 4     var td=document.createElement("td");
 5     td.appendChild(document.createTextNode(str));
 6     td.style.cursor="crosshair";
 7     var _this=this;
 8     td.onclick=function()
 9     {//考慮到瀏覽器可能擅自更改背景顏色樣式的字符串表示格式,使用一個不顯示的div進行比對
10         if(td.parentNode.style.backgroundColor!=_this.div_temp3.style.backgroundColor)
11         {//若是還沒變色
12             td.parentNode.style.backgroundColor=_this.pickColor;
13         }
14         else
15         {
16             if(_this.isStripe==1)
17             {
18                 //若是已經變色則恢復本來顏色
19                 if(parseInt(td.innerHTML)%2==0)
20                 {
21                     td.parentNode.style.backgroundColor = _this.baseColor;
22                 }
23                 else
24                 {
25                     td.parentNode.style.backgroundColor = _this.stripeColor;
26                 }
27             }
28             else
29             {
30                 td.parentNode.style.backgroundColor = _this.baseColor;
31             }
32         }
33     };
34     tr.appendChild(td);
35 }
36 //這個給遮罩層用,id是表實體的id
37 Att7.prototype.InsertaTDPick2=function (tr,str,id)
38 {
39     var td=document.createElement("td");
40     td.appendChild(document.createTextNode(str));
41     td.style.cursor="crosshair";
42     td.style.width="50px";
43     var _this=this;
44     td.onclick=function()
45     {//526DA5
46         if(td.parentNode.style.backgroundColor!=_this.div_temp3.style.backgroundColor)
47         {
48             td.parentNode.style.backgroundColor=_this.pickColor;//修改遮罩層
49             ChangeTable(td,_this.pickColor);
50         }
51         else
52         {
53             if(_this.isStripe==1)
54             {
55                 if(parseInt(td.innerHTML)%2==0)
56                 {
57                     td.parentNode.style.backgroundColor = _this.baseColor;
58                     ChangeTable(td,_this.baseColor);
59                 }
60                 else
61                 {
62                     td.parentNode.style.backgroundColor = _this.stripeColor;
63                     ChangeTable(td,_this.stripeColor);
64                 }
65             }
66             else
67             {
68 
69             }
70         }
71     };
72     function ChangeTable(obj,color)//遮罩層變化以後,原表格也要變化
73     {
74         var trs=document.getElementById(id).getElementsByTagName("tr")//找實體表而後去修改
75         var leng=trs.length;
76         for(var i=1;i<leng;i++)
77         {
78             if(obj.innerHTML==trs[i].getElementsByTagName("td")[0].innerHTML)
79             {
80                 trs[i].getElementsByTagName("td")[0].parentNode.style.backgroundColor=color;
81             }
82         }
83     }
84     tr.appendChild(td);
85 }

f、自定義多樣的單元格類型

att定義了多種經常使用的複雜報表單元格,也支持您添加本身的單元格類型,時間有限,這裏只舉兩個例子:

limit單元格:數據長度正常時原樣顯示,若是數據長度超過單元格寬度太多,則顯示縮略文本,同時在單元格里插入一個按鈕,點擊按鈕彈出小對話框顯示完整內容:

1 else if(data[2][m] == "limit")//限制字符長度不能過長
2                     {
3                         this.InsertaTDStr_lim(tr2, data[l][m - count],(data[3][m-count_none]+"px"));
4                         if(collock>0&&(m+1)<collock)
5                         {
6                             this.InsertaTDStr_lim(tr_mask, data[l][m - count],(data[3][m-count_none]+"px"));
7                         }
8                     }
 1 //限制寬度
 2 Att7.prototype.InsertaTDStr_lim= function(tr,str,wid,charwid)
 3 {//
 4     var td=document.createElement("td");
 5     td.style.width=wid?wid:"200px";
 6     td.style.position="relative";
 7     if(str==null)
 8     {
 9         str="";
10     }
11     var num_wid=parseInt(wid.split("px")[0]);
12     var input1 = document.createElement("input");
13     input1.type="text";
14     input1.style.border = 0;
15     input1.style.width =num_wid+"px" ;//控件寬度
16     input1.style.textAlign = "center";
17     input1.style.backgroundColor="transparent";
18     input1.style.float="left";
19     input1.value=str;
20     input1.readOnly=true;
21     /*input1.onfocus=function(evt){
22      this.blur();這樣就不能複製粘貼了!
23      }*/
24 
25     if(!charwid)//若是沒有設置字寬
26     {
27         charwid=10;
28     }
29     if((str.length*charwid)>(num_wid*2))//若是文字的長度超過了單元格寬度的兩倍
30     {
31         //td.title=str;
32         //td.overflow="hidden";
33         //str=(str.substr(0,(num_wid*2/10).toFixed()) +"...");
34         //嘗試在右側加一個彈出小按鈕?
35         //str=(str.substr(0,(num_wid*2/10).toFixed()) );
36         input1.style.width =(num_wid-30)+"px" ;
37         td.appendChild(input1);
38         var btn =document.createElement("button");
39         btn.className="btn_limlen";
40         btn.title=str;
41         var _this=this;
42         btn.onclick=function()//經過點擊打開的彈出框須要一個關閉按鈕,經過鼠標移入打開的彈出框則隨移出自動關閉
43         {
44             /*if(clipboardData) {
45              clipboardData.clearData();
46              clipboardData.setData("text", str);
47              }
48              else */
49             /*
50              if(event.clipboardData)
51 
52              {//火狐?
53              event.clipboardData.clearData();
54              event.clipboardData.setData("text/plain", str);
55              alert("內容已複製到剪貼板");
56              }
57              else if(window.clipboardData)
58              {//IE
59              window.clipboardData.clearData();
60              window.clipboardData.setData("text", str);
61              alert("內容已複製到剪貼板");
62              }
63              */
64             //clipboardData.getData("text");
65             var evt=evt||window.event||arguments[0];
66             cancelPropagation(evt);
67             var obj=evt.currentTarget?evt.currentTarget:evt.srcElement;
68             if(delete_div("div_bz")>0)//清空可能已經顯示的其餘小窗口
69             {
70                 //return;
71             }
72             Open_div("", "div_bz", 240, 120, 0, 0, obj, "div_tab");//自編的一個彈出小窗口方法(舊版)
73             //var target={top:,left:}//lim保持不變,嘗試添加lim2
74             //Open_div2("div_bz", "div_bz", 240, 120, 0, 0, obj, "div_tab");
75             document.querySelectorAll("#div_bz")[0].innerHTML = _this.html_onclick;//向彈出項裏寫入結構(以前初始化階段定義的小控件)
76             document.querySelectorAll("#div_bz .div_inmod_lim_content")[0].innerHTML = str;
77         }
78         td.appendChild(btn);
79     }
80     else
81     {
82         td.appendChild(input1);
83     }
84 
85     tr.appendChild(td);
86 }

select:單元格里是一個下拉框,用戶改變選項後觸發某些事件

 1 else if (data[2][m][0] == "select")//單元格是一個下拉框
 2                     {
 3                         var td2 = document.createElement("td");
 4                         //td2.style.width = "100px";
 5                         td2.style.width=(data[3][m-count_none]+"px");
 6                         var select = document.createElement("select");
 7                         select.className = data[2][m][1];
 8                         select.style.width = "100px";
 9                         select.selectedIndex=0;
10                         var temp_i=0;//用來暫存下面的i
11                         for (var i = 0; i < data[2][m][2].length; i++)
12                         {
13                             var option = document.createElement("option");
14                             option.innerHTML = data[2][m][2][i][0];
15                             if(data[2][m][2][i][1]) {//若是有的話也不介意設置一個value
16                                 option.value = data[2][m][2][i][1];
17                             }
18                             select.appendChild(option);
19                             if(data[2][m][2][i][1]==data[l][m - count]||data[2][m][2][i][0]==data[l][m - count])//後臺傳過來的多是value也多是text!!
20                             {//若是這個選項和數據集裏的數據相符,則默認把這個選項選中
21                                 option.selected="selected";
22                                 select.selectedIndex=i;
23                                 temp_i=i;
24                             }
25                         }
26                         listenEvent(select,"change",select_onchange);//監聽選項變化
27                         select.datachange=data[2][m][3];
28                         function select_onchange()
29                         {
30                             var evt = evt || window.event||arguments[0];
31                             cancelPropagation(evt);//發現若是不阻斷事件,會引起button1的click相應!!??
32                             var obj=evt.currentTarget?evt.currentTarget:evt.srcElement;
33                             //dataObj2[parseInt(this.parentNode.parentNode.firstChild.innerHTML)%150-1][parseInt(this.className.split("*")[1])]=this.value;
34                             eval((obj.getAttribute("datachange")?obj.getAttribute("datachange"):obj.datachange));
35                         }
36                         /*select.onchange=function()
37                          {
38                          var evt = evt || window.event;
39                          cancelPropagation(evt);//發現若是不阻斷事件,會引起button1的click相應!!??
40                          //dataObj2[parseInt(this.parentNode.parentNode.firstChild.innerHTML)%150-1][parseInt(this.className.split("*")[1])]=this.value;
41                          eval(data[2][m][3]);
42                          }*/
43                         td2.appendChild(select);
44                         tr2.appendChild(td2);
45                         if(collock>0&&(m+1)<collock)//對於遮罩層
46                         {
47                             /*var td2a = document.createElement("td");
48                              td2a.style.width=(data[3][m-count_none]+"px");
49                              var selecta=select.cloneNode(true);
50                              selecta.datachange=data[2][m][3];
51                              td2a.appendChild(selecta);*/
52                             var td2a=td2.cloneNode(true);//克隆dom元素
53                             var selecta=td2a.childNodes[0];
54                             selecta.datachange=data[2][m][3];
55                             selecta.selectedIndex=temp_i;
56                             tr_mask.appendChild(td2a);
57                             listenEvent(selecta,"change",select_onchange);
58                         }
59                     }

五、總結

基本完成了att的重構工做,舊版中使用jQuery的地方都替換成了原生的JavaScript方法,雖然原生方法的兼容性不如JQuery,但考慮到要配合兼容性更窄的WebGL使用,這些兼容性損失能夠忽略。重構的代碼沒有通過充分測試,可能存在各類問題,您能夠訪問https://ljzc002.github.io/Att/HTML/TEST/AttSample.html測試部分單元格類型。

相關文章
相關標籤/搜索