列表元件抽象(5):簡潔易用的表格元件

發表於2016-09-23

本文介紹如何實現一個簡潔易用的表格元件。

它對應的原始碼是:

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/mod/listView/tableView.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/mod/listView/tableDrag.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/mod/listView/tableOrder.js

https://github.com/liuyunzhuge/blog/blob/master/form/src/js/mod/listView/tableDefault.js

其中tableView是表格元件的核心。tableDrag和tableOrder是我寫的兩個外掛,分別讓表格支援列寬調整和自動生成序號列。tableDefault目前的作用僅僅是簡化外掛的配置。下面的demo可以讓你瞭解下它的基本功能:

http://liuyunzhuge.github.io/blog/form/dist/html/tableView.html

效果如下:

459873-20160921012044762-551259764

對應的演示程式碼為:

http://liuyunzhuge.github.io/blog/form/dist/js/app/tableView.js

在瞭解它的詳細實現思路前,你可以通過上面的演示程式碼來檢視這個元件的使用方式。整體上它與其它列表元件的用法類似,但是由於表格元件在結構和功能上的特異性,所以它在例項化的時候要用到好幾個其它列表元件不具備的option。例項程式碼如下:

補充:在demo中,我用heightFixed這個option用來表示是否要固定表格的高度;用plugins這個option來配置當前例項要擴充套件的外掛功能,利用到了tableDefaults來註冊預設的外掛組;最後通過jquery的形式給表格行內的某個dom元素繫結了點選事件,並在回撥內通過表格元件的例項化方法getRowData獲取到了該行對應的表格資料。

這個表格元件除了支援分頁查詢,排序之外,還支援以下功能:

1. 序號列生成,列寬調整,理論上可以通過自定義外掛的方式再擴充套件更多的功能;比如樹形表格、表格編輯等;可直接通過表格例項,對外掛進行增刪改查;

2. 自由切換是否固定表格高度,當表格高度固定時,表頭會固定,然後表體會以auto模式控制滾動條;滾動時,表頭由於固定所以不會被遮擋,便於使用者檢視錶格資料;如果表格高度不固定,那麼表體就不會出現滾動條,表頭固定也就沒有意義了;

3. css完全靈活,可通過option改變表格元件內部實現時需要的所有css class;

4. html結構相對靈活,表頭和表體的html都以模板的形式定義,如需在表頭和表體中插入相對個性化的內容,直接在模板中插入即可;

5. 支援表格行,單選和多選;當然兩種模式只能用其一;

6. 可方便地根據表格行的索引,獲取該行對應的原始資料和解析後的資料;原始資料就是ajax返回後未經parseData這個option處理的資料;解析後的資料就是parseData處理後的資料;

7. 可方便地獲取所有選中行的單個屬性值;

8. 當視窗resize,DOM更改等影響到表格內容的時候,表格的佈局會自動調整;也支援手動觸發調整;

總的來說,這個元件的實現並不麻煩,就是因為要實現的功能多,所以內容也多。

首先,先來了解下它html結構:

它把表格元件分成表頭、表體、表尾三部分。表頭顯示錶格標題行;表體顯示資料;表尾顯示分頁元件。因為要考慮做表頭固定,所以標題行和資料行不能屬於同一個table元素。固定只能利用絕對定位來做。

在設定css的時候,邊框和表頭的背景色的設定比較關鍵:

1. 不管是表頭的table和表體的table,都沒有上下左右邊框,你看到的邊框都是由table的包裹元素設定的。這麼做的目的,是為了表格元件在UI上的整體性考慮的。

2. 表頭的背景不是設定在表頭的table上,而是設定在table的包裹元素上。這麼做的原因還是跟UI整體性有關,當表體出現滾動條時,表體內的table的寬度變窄,為了讓表頭內的table寬度與表體內table的寬度保持一致,必須給表頭新增一個padding-right,並且大小為瀏覽器滾動條的寬度:

459873-20160921012045840-767746699

如果表頭的背景直接設定在table上,那麼padding-right那個位置,將讓人認為是頁面上多餘的空間,看起來彆扭。

接下來看看錶格元件原始碼中的要點。

1)defaults如下:

需要說明的有:

allCheckboxClass和rowCheckboxClass對應全選和單選的checkbox,由於checkbox並不是在元件內部寫死的,而是定義在模板內,所以必須通過選擇器才能使用。這個比直接把checkbox寫在元件內部的好處是,大大增加增加元件的靈活性;

plugins在配置的時候,用字面量物件來傳遞單個外掛的定義。如{name: “tableDrag”,plugin: TableDrag, options: {…}},name用來標識外掛例項,方便管理外掛;plugin表示外掛的建構函式,options傳遞外掛需要的option。

2)表格元件的初始化方法都比較簡單,主要是根據tableHd,colgroup這些option初始化表格元件的DOM結構;初始化佈局;初始化行選擇的功能;初始化外掛例項。

3)setRowSelected是一個例項方法,接受表格行的jq物件,並將其設定為選中狀態。外部也可直接呼叫它來實現手工選中行。

4)setUpTableSelect是一個內部用的例項方法, 初始化行選擇的功能。

5)getSelectedTrs是一個例項方法,返回選中行的jq物件。外部可使用。

6)getSelectedIndexs是一個例項方法,返回選中行的索引,陣列形式。外部可使用。

7)getRowData是一個例項方法,傳入一個索引,返回該索引對應行的經過parseData解析後的資料。

8)getOriginalRowData是一個例項方法,傳入一個索引,返回該索引對應行的原始資料。

9)getFields是一個例項方法,傳入一個屬性名稱,返回所有選中行的解析後的資料中該屬性的值。

10)getOriginalFields是一個例項方法,傳入一個屬性名稱,返回所有選中行的原始資料中該屬性的值。

11)getPlugin是一個例項方法,傳入外掛定義時的name值,即可返回該外掛的例項,

12)addPlugin是一個例項方法,用來手工例項化一個外掛,它接收一個滿足plugins option元素要求的物件,用來例項化外掛。

每個外掛例項化的時候,都會給它的建構函式,傳入兩個引數,一個是表格元件本身,另外一個就是外掛相關的options。意味著所有的外掛的建構函式都得按這個形式來。

13)removePlugin是一個例項方法,用來銷燬某個外掛的例項。銷燬除了要考慮功能的取消邏輯,還要考慮好記憶體洩漏的問題,所以一定要檢查外掛所有的可能會導致記憶體洩漏的地方,尤其是那些繫結的事件。這個方法內部會通過呼叫外掛的destroy方法來完成銷燬,所以在定義外掛的時候最好是提供這樣一個方法:

14)adjustLayout是一個例項方法。初始化完畢,瀏覽器視窗調整,以及查詢完畢之後都會主動呼叫,以更新table的UI佈局。外部也可直接呼叫,尤其是在外部更改table的DOM內容,而table不知道的情況下,以防UI錯亂。

從程式碼可看出,它主要做的有以下幾件事情:

adjustPaddingTop: 由於表頭固定,採用絕對定位,所以表格元件整體得設定padding-top,這個值在DOM變化的時候就要更新,防止表頭蓋住表體;

adjustTableHdViewPos: 由於表頭固定,當表體橫向滾動時,靠它來更新表頭的位置,以便表頭的每一列都能跟表體的每一列對齊;

adjustTableBdViewHeight: 跟第一個同理,表頭高度變化後,在表格高度固定時,表體高度也會變化,所以要重新設定表體的高度,以便瀏覽器更新overflow的狀態;

checkTableBdScrollState: 檢查表體是否出現橫向滾動,如果是,則給表頭新增寬度等於滾動條寬度的padding-right,否則就去掉。

以上就是表格元件的核心要點了。單個點相關的程式碼邏輯都不是特別複雜,所以大部分都沒有特意給出相應原始碼說明。

再來說說預設外掛之一:tableOrder的定義。

它只有一個option:

作用完全類似於checkbox那兩個option,外掛利用它找到合適的位置顯示序號。

這個類很簡單:

只要根據pageView例項,拿到它的data屬性就能使用其中的start,end來生成序號,start,end分別表示當前請求的記錄範圍的起止索引。它提供了destroy方法,以防有需要銷燬的場景。然後它的建構函式也是含之前介紹表格元件的addPlugin方法時的說明來的,第一個參數列格元件的例項,第二個引數options。

最後再來說預設外掛之一:tableDrag的定義。

這個相對邏輯多一點。我這裡也只介紹我的思路。

1)生成拖拽的“把手”。我這裡是用一個空的元素,通過絕對定位的方式,顯示在每個單元格的右邊框上來處理的。當滑鼠移上去變成可拖拽的模式時,點選滑鼠拖動,就能調整列寬。

2)列在拖拽過程中,寬度的變化,我都是colgroup中對應的col元素來實現的。而不是直接通過控制td的寬度。這種方式也更符合標準。

3)這個元件裡面有一處比較關鍵的程式碼:

它的作用是監聽表格元件的adjustLayout事件,在事件回撥內更新所有col的寬度。之所以這麼幹,是因為表格的列寬是會自動調整的,尤其在表格佈局改變之後,列寬的實際寬度不一定等於col上定義的寬度,所以要在表格佈局改變的時候重新計算各個col的寬度,下次拖拽的結果才能正確。

具體拖拽的實現邏輯就跟平常做的那些拖拽沒區別了:在滑鼠點下的時候記好位置,拖動過程中,用最新的滑鼠位置與最初的位置,就能得到拖拽實時的偏移距離。然後再計算到col的寬度上即可。

到此為止的話,表格元件的一些要點也介紹完畢,關於列表元件的這一大堆檔案的分享,基本上也就到此結束了,將來我自己肯定還會不斷地完善,但那更多是在專案中的實際工作了,不一定還會再寫出來,畢竟每個專案都有個性化的需求。我寫這些東西的初衷,是認為列表功能,分頁功能,排序功能之間都有相似性,為了減少重複程式碼,可以做一些抽象,來讓程式碼更加簡潔,更好管理。希望關於列表元件的內容能給大家帶來一些有價值的東西。謝謝這幾天的關注:)

相關文章