列表元件抽象(4):滾動列表及分頁說明

發表於2016-09-20

本文介紹列表元件中我對滾動列表及滾動分頁的實現思路。

在pc端,通過滾動進行翻頁的需求非常常見;移動端也是,只不過移動端由於scroll事件觸發有延遲,必須等到螢幕停止滑動後才會觸發,而不是在使用者的手指離開螢幕就立即觸發,所以移動端最好是不用scroll事件直接做滾動翻頁,而是用iscroll這類外掛提供更實時的scroll事件更好。

不管是pc還是移動端,滾動翻頁列表的特點都是差不多的:

1)基本上由以下幾個部分組成:資料列表,頂部的載入中提示,底部的載入中提示,沒有更多了,沒有找到記錄。正是按照這個思路,所以我把滾動列表的html結構設計成:

459873-20160919221055340-814212948

2)跟其它列表元件不同的是,滾動列表在請求新的資料後,有2種方式來渲染新的資料。一種是跟其它列表元件一樣,直接把原來的列表內容替換;另一種是將新資料追加在原有的列表內容之後。第1種通常用於直接更改列表的查詢條件時使用;第2種用於翻頁查詢或者重新整理操作。

3)在前面的幾個部分中,有兩個載入中的提示,都是用來提升使用者體驗的東西。頂部載入提示用於條件查詢,底部載入提示用於翻頁查詢。從它們在html中的位置也能看出來。

4)載入更多的按鈕,一是防止滾動事件失效而準備的,二是有些場景可能會禁用掉滾動翻頁,所以就要提供直接點選按鈕的手工翻頁。

5)沒有更多了這個部分,在翻頁查詢後,根據資料結果判斷沒有更多的資料時顯示。

6)沒有找到記錄的這個部分用於在列表首次查詢時,如果資料為空時顯示。

7)當通過滾動或者滑動操作,使得滾動列表隱藏於可視區域之下的部分不斷往上滾動,並在達到某一個臨界點的時候,觸發翻頁查詢,將下一頁的資料追加到資料列表後面進行顯示。

針對以上的這些需求邏輯,考慮pc端和移動端的場景,我寫了兩個元件分別用於實現滾動列表。同時與這兩個列表元件一起使用的還有另外兩個分頁元件,它們兩兩之間是配套使用的。

首先是用於實現pc端,可相對window或者某個DOM元素進行滾動分頁的列表元件scrollListView以及它配套的分頁元件scrollPageView元件,原始碼分別是:

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

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

然後是用於移動端,結合iscroll一起使用的iscrollListView和iscrollPageView元件,原始碼分別是:

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

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

針對以上元件有以下demo可以檢視相關功能演示:

pc端相對window滾動分頁demo:http://liuyunzhuge.github.io/blog/form/dist/html/listView_2.html

pc端相對某個DOM元素滾動分頁demo:http://liuyunzhuge.github.io/blog/form/dist/html/listView_3.html

移動端滾動分頁demo:http://liuyunzhuge.github.io/blog/form/dist/html/listView_4.html

後面的部分說明以上元件的要點。不過由於iscrollListView直接繼承了scrollListView,實現非常簡單;iscrollPageView的實現思路也跟scrollPageView差不多。所以後面只介紹scrollListView和scrollPageView的相關內容。

先來看scrollListView.js。

首先,程式碼結構還是跟前幾篇部落格介紹的元件都差不多,所以這裡不再複述。defaults是這樣定義的:

主要是用來定義滾動列表的那幾個組成部分。如果不想用預設值,那麼在例項化元件的時候,傳入想要設定的option就行了。

scrollListView繼承了listViewBase,為了增加自己的初始化邏輯,所以用到了initMiddle這個模板方法,並在其中做了一些jq物件快取,以及內部狀態管理初始化的邏輯:

以上程式碼中的那個states物件用來實現內部的狀態管理,可以看作一個簡單的狀態機。採用這個做法的原因,一是為了滿足最前面介紹滾動列表元件特點時描述的那些需求邏輯,二是為了讓這些UI控制邏輯看起來更清晰。有了它,我只要在何時的時機,改變下元件的狀態,就能列表元件顯示不同的內容了。比較簡單好理解。

然後通過createPageView來實現分頁元件的初始化邏輯。這裡就得使用scrollPageView來例項化了:

然後把scrollPageView需要的幾個dom物件以option的形式傳給了它。

考慮到滾動列表元件的特殊性,我還用到了listViewBase的其它幾個模板方法來實現滾動列表的需求。

首先是beforeQuery:

這個方法會接受一個引數clear,為true則表示進行條件查詢,否則表示進行翻頁查詢。這個方法的作用在於查詢前顯示載入提示。

然後是querySuccess:

它用來實現請求成功的後的邏輯,最重要的當然是渲染資料。但是考慮到列表元件的需求,還得根據多方面的引數,判斷該把列表設定為什麼樣的狀態。請求成功後的結果無非三種,沒有查到資料,沒有更多,載入成功。這三個狀態,根據分頁資訊和記錄數就能判斷清楚,見原始碼裡面if邏輯的寫法。

然後是queryError:

這個主要是在請求失敗的時候,還原列表的狀態而已。

最後是afterQuery:

它在請求完成之後,判斷如果是沒有資料或者沒有更多的狀態的話,就禁用掉分頁元件,免得使用者操作不慎導致還會發出一些查不到資料的請求。

以上就是scrollListView實現的核心了,只有100多行。

再來看scrollPageView。

它的defaults定義如下:

應該好理解。offset的作用後面會繼續說明,scrollBindDelay是用來延遲滾動事件繫結的。為啥會搞這個,是因為chrome瀏覽器有個特性,如果在瀏覽網頁的時候,重新整理之後,滾動條會恢復到重新整理前瀏覽的位置,並且它這個自動恢復也會觸發滾動事件。那麼當列表元件初始化完畢之後,很有可能會發出兩次查詢請求,一次是由初始化呼叫發出的,一次是由自動恢復的滾動事件發出的。所以加上這個option,有利於控制列表初始化後的首次請求。$loadMore用於註冊點選事件,新增手動翻頁的邏輯。$element表示滾動列表相關的dom物件。$target表示滾動相對的目標物件,如果不傳,就指向window物件。

scrollPageView內部提供了簡單的節流函式來做滾動事件回撥的節流控制:

也提供了獲取css屬性在瀏覽器重繪之後的值的函式:

其它程式碼倒是沒啥好補充的,重點看下滾動事件相關的翻頁控制邏輯,我就只貼了相關的匿名函式程式碼了:

以上的思路也比較簡單,只要判斷列表元素的底部跟目標物件的可視區域的底部達到臨界距離即可,這個臨界距離就是defaults中定義的offset值。以相對window滾動為例說明如何來做這個判斷:

459873-20160919221056356-1792239646

根據上圖,可以得知翻頁臨界的判斷條件就是上圖中臨界線到目標物件可視區域頂邊的距離小於目標物件可視區域的高度。這個圖雖然是以相對window的滾動情況來說明問題的,但是相對DOM物件進行滾動的判斷方式跟這個是一模一樣的,只要我們能夠得到DOM物件的可視區域高度以及臨界線到DOM物件可視區域頂邊的距離即可。我的程式碼中是利用getBoundingClientRect來求的,相當於還是以瀏覽器的可視區域的頂邊作為參考線,不過考慮到普通的DOM物件可能也有頂部邊框的問題,在計算最後的臨界線到DOM物件可視區域的頂邊距離時,減去了DOM物件頂部變寬的寬度。只有這樣得出的臨界線距離才是相對於DOM物件可視區域頂邊而言的。

以上就是本文的全部內容,介紹了我想要補充說明的關於滾動列表元件的部分。這一塊內容,我覺得沒有特別廣的適用性,畢竟各個產品對滾動翻頁這種需求的邏輯可能也不盡相同,我這邊提供的是我現有專案中的實現思路,可能只能對您有一定的參考價值。所以要是有不妥的,歡迎隨時幫我指正出來。謝謝閱讀:)

相關文章