jQuery FlexSlider外掛一些使用上的小tips
最近做的一個需求,用到了jQuery FlexSlider
這個外掛,本以為一個算是已經比較成熟的外掛,用起來應該沒什麼難度,然而真正用起來才發現,其實還是有些坑的,不過好在這個外掛原始碼寫的比較好,比較好看,所以自己多看看也就能弄明白。
網上那些隨便一搜就能找到的你抄我我抄你大家互相抄的東西,我就不繼續浪費網路資源和自己的時間了,主要是一些網上很難找到的或者比較模稜兩可的點,這裡以jQuery FlexSlider v2.6.3
這個版本為例,總結如下。
回撥方法
網上一些提到 flex-slider
外掛的方法,基本上都是一帶而過,而且模稜兩可,我估計當時作者不知從哪裡看到然後隨手 copy下來的時候,連他自己都不知道自己在寫些什麼。
一共 7
個回撥函式,如下:
start: function(){}
原始碼裡面標註的解釋是:當第一個滑塊第一次載入完畢後執行。 說白了就是當此元件初始化(init)完成後,然後又當第一個滑塊載入完畢後,此函式執行,在當前slider
元件的整個生命週期中,只執行一遍。before: function(){}
在滑塊運動之前的一刻執行,也就是說當你點選切換當前輪播項的時候,此函式會在輪播項開始運動的前一刻執行。after: function(){}
和上一個相反,在滑塊運動完成的下一刻執行,也就是說當你點選切換當前輪播項的時候,此函式會在輪播項完成運動的下一刻執行。end: function(){}
當前整個輪播元件滑動到了最後一個滑塊的時候執行,與滑塊的運動動作非同步並行執行,也就是說是在before
和after
函式之間執行,每次到達尾部,此函式都會執行一遍。added: function(){}
當新的滑塊被追加到輪播元件中後,就會呼叫此回撥函式。removed: function(){}
當輪播元件中有滑塊被刪除掉後,此函式會執行init: function() {}
當輪播元件初始化完成後執行,此函式實際上不僅只在開始的時候和執行一遍,如果第一次初始化完後,繼續向輪播元件中追加滑塊,則此函式還可能會被呼叫,所以在輪播元件的整個生命週期中,此函式被呼叫的次數 >=start
函式被呼叫的次數。
非同步獲取資料初始化
如果輪播元件的資料是非同步獲取而來,則很顯然在資料尚未獲得之前,flex-slider
是不能夠進行初始化的,否則將會出現異常情況。
對於這種情況,有兩種常用的解決手段,一是使用回撥函式,二是使用 Promise
通過回撥函式初始化元件
首先頁面傳送獲取資料的的請求,當資料成功獲取後,再在回撥函式中進行 flex-slider
的初始化。
如下示例(為了簡化程式碼,使用到了 Jquery
庫):
$(function(){
getData(){
$.getJSON('http://api.example.com&callback=?', (data)=>{
// 這裡可以放初始化`flex-slider`的程式碼
// ...
})
}
})
通過 Promise
初始化元件
如果不方便在回撥函式中執行 flex-slider
元件的初始化才做,或者希望以同步的寫法來進行元件的初始化,那麼 Promise
值得一用。
首先依舊是需要首先請求資料,但是回撥函式中不執行初始化操作,而是返回一個 Promise
物件:
$(function(){
getData(){
return new Promise((resolve, reject)=> {
$.getJSON('http://api.example.com&callback=?', (data)=>{
// 將獲取到的資料當成 `Promise`的引數傳回去
resolve(data)
})
})
}
})
然後就能保證在初始化 元件之前獲取到資料了:
let promiseResult = getData()
promiseResult.then((data)=>{
// 這裡可以放初始化`flex-slider`的程式碼
// ...
})
當然還可以使用 async
函式來完成非同步操作的同步操作,如果需要相容不支援 ES6
的瀏覽器,則最後別忘了使用一些工具將 ES6
程式碼轉換成 ES5
的形式。
非同步增刪資料項
如果 flex-slider
元件的滑塊數量是固定的,而且還不用懶載入,是一次性載入初始化完畢的,那麼沒什麼好說的,但是如果在元件第一次完成初始化後,還將會動態地向元件內增加 slider
,也就是滑塊的數量是動態變化的,那麼就需要用到 flex-slider
另外的方法了。
動態追加資料
需要用到 flex-slider
的 addSlide()
方法,此方法程式碼很簡單:
要做的事情也很清楚:
首先更新元件的滑塊數量,然後重新將元件初始化一遍,最後呼叫
added()
這個回撥函式。
從addSlide()
方法中可以看出,動態增加滑塊數量,是會引起元件重新初始化的,所以 init:function(){}
回撥函式會執行,這也就解釋了為什麼 init
並不總是隻執行一遍的原因。
從上述程式碼中可以看到,addSlide()
方法其實是放在 slider
物件上的,想用addSlide()
方法,就必須要有 slider
物件才行,有人可以不清楚這個slider
到底是什麼鬼,其實這個物件,可以在前面所說的 7個回撥函式中獲取,因為這個slider
就是那 7個回撥函式的引數。
例如:
$("#flexSlider").flexslider({
start(slider): function(){
// 這裡就可以使用 addSlide()方法
...
slider.addSlide(obj, pos);
}
})
其他 6個回撥函式都有 slider
這個函式,和 start
一樣。
至於 addSlide
的兩個引數,一個是必選引數obj
,第二個則是可選的。
obj
此引數代表的就是你想要動態追加的滑塊HTML
字串,例如<li></li>
,在被傳入addSlide
方法中後,會被自動包裝成Jquery
物件。pos
你可以通過 此引數來選擇將動態插入的滑塊HTML
插入到原有的一些滑塊的什麼位置,如果你不傳入此引數,則預設追加到原有元件的末尾處。
- 關於
addSlide
方法的簡單改進
從 addSlide
的實現方法中可以看到,此方法每次只能動態新增一個滑塊,數量每次只能加 1
,如果你想一次性新增多個滑塊,則必須多次呼叫此函式,也就會多次執行對 flex-slider
的初始化操作,這顯然很影響效能。
而且據我測試,連續多次呼叫此函式,flex-slider
的初始化會出現異常,例如無法正確計算出每次滑動的距離,導致元件佈局崩潰。
所以我就簡單對這個方法改進了一下,讓其能夠一次性插入多個滑塊的DOM
,但是卻僅在最後重新初始化一次,如下:
此函式只是將原先每次固定的只能追加一個滑塊,變成了能夠自由選擇數量,其餘的一些操作依舊不變。
四個引數中 obj
和 pos
的含義和作用和原先一樣,至於 slider
則和上面的 slider
指的是同一個物件,都是那 7個回撥函式的引數,這是為了方便通過此物件來呼叫我們自己重寫的 addSlider
方法,number
值得就是你需要新增的滑塊數量,預設為 1
。
用法示例如下:
$("#flexSlider").flexslider({
// 這裡我們假設當每次元件滑動到最後一個滑塊的時候,就追加新的滑塊
// 有點類似於懶載入或者預載入的意思
end(slider): function(){
// 假設每一個 `li`元素,就是一個滑塊的單位,這裡我們一次性追加了三個滑塊的 `DOM`
let str = '<li></li><li></li><li></li>'
// 呼叫我們重寫的 addSlide() 方法,這裡我們一次性新增了三個滑塊,
// 所以傳入數字 3
addSlide(slider, str, 3);
}
})
- 追加滑塊時出現的問題
如果是理想情況下,新增滑塊的資料是通過同步方法一次性順序獲得,那麼一切正常。
但不知是不是我用法不太對,如果是通過非同步請求資料的方法來取得新增滑塊的資料,無論我是通過 flex-slider
提供的 addSlide
方法,正常地每次新增一個滑塊,還是使用自己改進的 addSlide
方法來一次性新增多個滑塊,亦或是我通過回撥函式還是 Promise
的方法來請求非同步資料,甚至我把新增滑塊的動作,在 7個回撥函式中都試了一遍,最終的結果,都是 flex-slider
元件無法正常初始化,無法正常計算每次需要滑動的距離。
我明明想讓它每次滑動三個滑塊的距離,結果它卻滑動了一個半,甚至還給我來了個漂移地來回滑動,導致佈局崩潰。
最後折騰了好長時間,試了好多種方法,但好在總算是讓我找出了一個還算是好用的方法。
道理很簡單,既然如果是同步請求資料來追加新的滑塊,元件就能夠正常初始化,那麼我們就假裝讓元件中追加的新滑塊資料是同步得來的就行了:
先用沒有任何內容的滑塊
DOM
(例如空的li
標籤)來佔位,讓flex-slider
元件正常新增滑塊,這樣元件就能正常初始化,能夠正確計算每次滑動的距離,一切正常。
然後在此基礎上,在稍後獲取到非同步資料後,將獲取到的真正的滑塊資料,替換掉之前的佔位空滑塊,這樣對於flex-slider
元件來說,滑塊的數量並沒有任何變化,也就不會引起其任何的動作,所以替換完成後,元件依舊是正常的。
下面用程式碼說話。
假設我們非同步獲取新增滑塊資料的方式是通過 Promise
,每次到達元件末尾的時候,就自動獲取新的資料,追加新的滑塊。
$("#flexSlider").flexslider({
// 這裡我們假設當每次元件滑動到最後一個滑塊的時候,就追加新的滑塊
// 有點類似於懶載入或者預載入的意思
end(slider): function(){
// 假設每一個 `li`元素,就是一個滑塊的單位,這裡我們一次性追加了三個空滑塊的 `DOM`,
// 這三個 `DOM`只有一個作用,那就是給將來真正要呈現在這裡的滑塊佔位,
// 以便於 `flex-slider`元件正常的重新初始化,不會產生各種各樣的異常,將來是要被替換掉的。
let str = '<li></li><li></li><li></li>'
// 呼叫我們重寫的 addSlide() 方法,這裡我們一次性新增了三個滑塊,
// 所以傳入數字 3
addSlide(slider, str, 3);
let promiseResult = getData()
promiseResult.then((data)=>{
// 假設 data 中有動態追加滑塊的資料
// len 指的是新增滑塊的數量
let len = data.length
for(let i=0;i<len;i++){
// 在這裡組裝真正的滑塊DOM
let newDOM = ...
// 關鍵在這裡,將原先的佔位滑塊DOM,替換成真正想要展示的
// 這裡假設是追加到 flex-slider 的末尾處
$('#flexSlider .slides li:eq('+(slider.count-j)+')').replaceWith(str)
}
})
}
})
動態刪減資料
刪除資料使用到另外一個方法 :removeSlide
:
此方法做的事情,基本上都是一樣的,都重新指定了刪減後滑塊的數量,以及重新初始化,並且在最後呼叫了 removed
回撥函式。
這個方法只有一個 obj
引數,代表的意思與 addSlide
相同,沒有第二個引數,也就是不能隨意指定要刪除哪個滑塊,只能是最後一個。
不過我們同樣可以自己將此方法重寫一遍,讓其能一次性刪除多個滑塊,並且能夠指定要刪除的滑塊下標,我沒有實際做過,期間可能還會有其他的異常,但大致思路是這樣的肯定沒錯。
removeSlide
方法的用法與 addSlide
沒有太大區別,就不多說了。