1. 程式人生 > >jQuery FlexSlider外掛一些使用上的小tips

jQuery FlexSlider外掛一些使用上的小tips

最近做的一個需求,用到了jQuery FlexSlider這個外掛,本以為一個算是已經比較成熟的外掛,用起來應該沒什麼難度,然而真正用起來才發現,其實還是有些坑的,不過好在這個外掛原始碼寫的比較好,比較好看,所以自己多看看也就能弄明白。

網上那些隨便一搜就能找到的你抄我我抄你大家互相抄的東西,我就不繼續浪費網路資源和自己的時間了,主要是一些網上很難找到的或者比較模稜兩可的點,這裡以jQuery FlexSlider v2.6.3這個版本為例,總結如下。

回撥方法

網上一些提到 flex-slider外掛的方法,基本上都是一帶而過,而且模稜兩可,我估計當時作者不知從哪裡看到然後隨手 copy下來的時候,連他自己都不知道自己在寫些什麼。

一共 7個回撥函式,如下:

回撥函式API

  1. start: function(){}
    原始碼裡面標註的解釋是:當第一個滑塊第一次載入完畢後執行。 說白了就是當此元件初始化(init)完成後,然後又當第一個滑塊載入完畢後,此函式執行,在當前slider元件的整個生命週期中,只執行一遍。
  2. before: function(){}
    在滑塊運動之前的一刻執行,也就是說當你點選切換當前輪播項的時候,此函式會在輪播項開始運動的前一刻執行。
  3. after: function(){}
    和上一個相反,在滑塊運動完成的下一刻執行,也就是說當你點選切換當前輪播項的時候,此函式會在輪播項完成運動的下一刻執行。
  4. end: function(){}
    當前整個輪播元件滑動到了最後一個滑塊的時候執行,與滑塊的運動動作非同步並行執行,也就是說是在 beforeafter 函式之間執行,每次到達尾部,此函式都會執行一遍。
  5. added: function(){}
    當新的滑塊被追加到輪播元件中後,就會呼叫此回撥函式。
  6. removed: function(){}
    當輪播元件中有滑塊被刪除掉後,此函式會執行
  7. 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-slideraddSlide()方法,此方法程式碼很簡單:

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,第二個則是可選的。

  1. obj
    此引數代表的就是你想要動態追加的滑塊 HTML字串,例如 <li></li>,在被傳入 addSlide方法中後,會被自動包裝成 Jquery物件。
  2. pos
    你可以通過 此引數來選擇將動態插入的滑塊HTML插入到原有的一些滑塊的什麼位置,如果你不傳入此引數,則預設追加到原有元件的末尾處。
  • 關於 addSlide方法的簡單改進

addSlide的實現方法中可以看到,此方法每次只能動態新增一個滑塊,數量每次只能加 1,如果你想一次性新增多個滑塊,則必須多次呼叫此函式,也就會多次執行對 flex-slider的初始化操作,這顯然很影響效能。

而且據我測試,連續多次呼叫此函式,flex-slider的初始化會出現異常,例如無法正確計算出每次滑動的距離,導致元件佈局崩潰。

所以我就簡單對這個方法改進了一下,讓其能夠一次性插入多個滑塊的DOM,但是卻僅在最後重新初始化一次,如下:

改進 addSlider方法

此函式只是將原先每次固定的只能追加一個滑塊,變成了能夠自由選擇數量,其餘的一些操作依舊不變。

四個引數中 objpos 的含義和作用和原先一樣,至於 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 沒有太大區別,就不多說了。