欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Sticky組件的改進(jìn)實(shí)現(xiàn)

 更新時(shí)間:2016年03月22日 09:40:17   作者:流云諸葛  
這篇文章主要介紹了Sticky組件的改進(jìn)實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下

在上一篇文章使用getBoundingClientRect方法實(shí)現(xiàn)簡潔的sticky組件的方法介紹了一個(gè)sticky組件的簡潔實(shí)現(xiàn),經(jīng)過這兩天的思考,發(fā)現(xiàn)上次提供的實(shí)現(xiàn)還有較多不足的地方,另外跟別的網(wǎng)站上實(shí)現(xiàn)的效果在取消固定的時(shí)候也有一些不同,上次提供的取消固定的處理方式不好,本文在上文的基礎(chǔ)上,提供一個(gè)改進(jìn)版的sticky組件,功能更加完善,希望您有興趣閱讀。

1. 舊版本的問題

上一個(gè)sticky組件的實(shí)現(xiàn)中,有多個(gè)問題存在:

第一,從sticky的效果上來說,sticky元素在固定前后,不會(huì)變化的是相對瀏覽器左邊的位置以及sticky元素的整體寬度,可能會(huì)變化的是相對瀏覽器頂部或底部的位置和sticky元素的高度,而上文提供的實(shí)現(xiàn)中把后面兩個(gè)會(huì)變化的值都當(dāng)成了不變的值。為什么在固定的時(shí)候top值或bottom值就一定是0?當(dāng)然可以不是0阿,比如top: 20px,bottom: 15px,在某些場景里,加上一些這樣的偏移,sticky的效果會(huì)更好看,比如bootstrap官方文檔中用到的affix組件實(shí)例(這個(gè)組件的功能跟本文實(shí)現(xiàn)的sticky組件是差不多的):

它就把固定的時(shí)候,相對瀏覽器頂部的位置設(shè)置成了top: 20px。sticky元素的高度也是,為了在固定的時(shí)候顯示更好看的效果,調(diào)整原來的Line-height或者padding-top等更高度有關(guān)的屬性,也是非常常見的需求,比如天貓花唄的這個(gè)頁面,這塊內(nèi)容就用到了sticky組件:

固定前,sticky元素的高度是:

固定后,sticky元素的高度是:

第二,在取消固定的時(shí)候,以sticky元素固定在頂部為例,上文提供的實(shí)現(xiàn)是在target元素跟瀏覽器頂部的距離小于stickyHeight的時(shí)候,就直接取消sticky元素的position: fixed屬性,sticky元素立馬被還原到普通文檔流中,效果是:

它是在臨界點(diǎn)的時(shí)候立馬就消失的,而天貓花唄的那個(gè)效果就不是這樣:

它在臨界點(diǎn)的時(shí)候并不是立即消失,而是重新去調(diào)整sticky元素的top值,讓它配合著滾動(dòng)條一起跟隨網(wǎng)頁主體內(nèi)容一起向上滾動(dòng):

從體驗(yàn)上來說,顯然天貓花唄的這個(gè)效果更好一點(diǎn),從功能上來說,上文提供的實(shí)現(xiàn)有一個(gè)致命的缺點(diǎn):就是當(dāng)sticky元素的高度非常大,超出了瀏覽器可視區(qū)域的高度的時(shí)候,會(huì)出現(xiàn)不管你怎么滾動(dòng),都無法瀏覽全sticky元素所有內(nèi)容的BUG,有興趣的可以拿上次實(shí)現(xiàn)的代碼在自己博客的側(cè)邊欄上試一試。我試過發(fā)現(xiàn)了這個(gè)問題,所以才想要改進(jìn)sticky組件:(
第三,上次的實(shí)現(xiàn)還有幾處不足的地方:

1)documentElement.clientHeight沒有做緩存,導(dǎo)致每次判斷臨界點(diǎn)時(shí)都要去重新獲取:

2)滾動(dòng)回調(diào)間隔的默認(rèn)值太大,應(yīng)該再設(shè)置小一點(diǎn),這次用的是5,bootstrap用的是1,只有這樣才能保證效果流暢;

3)有的場景可能不需要resize的時(shí)候重新設(shè)置sticky元素的寬度,應(yīng)該加個(gè)選項(xiàng)來控制;

4)在sticky元素固定和取消固定的時(shí)候,應(yīng)該提供回調(diào)函數(shù),以便其它組件依賴這個(gè)組件的時(shí)候可以在關(guān)鍵點(diǎn)做些事情。

2. 如何改進(jìn)

組件的選項(xiàng)重新定義了一下:

var DEFAULTS = {
target: '', //target元素的jq選擇器
type: 'top', //固定的位置,top | bottom,默認(rèn)為top,表示固定在頂部
wait: 5, //scroll事件回調(diào)的間隔
stickyOffset: 0, //固定時(shí)距離瀏覽器可視區(qū)頂部或底部的偏移,用來設(shè)置top跟bottom屬性的值,默認(rèn)為0
isFixedWidth: true, //sticky元素寬度是否固定,默認(rèn)為true,如果是自適應(yīng)的寬度,需設(shè)置為false
getStickyWidth: undefined, //用來獲取sticky元素寬度的回調(diào),在不傳該參數(shù)的情況下,stickyWidth將設(shè)置為sticky元素的offsetWidth
unStickyDistance: undefined, //該參數(shù)決定sticky元素何時(shí)進(jìn)入dynamicSticky狀態(tài)
onSticky: undefined, ///sticky元素固定時(shí)的回調(diào)
onUnSticky: undefined ///sticky元素取消固定時(shí)的回調(diào)
};

加粗的幾個(gè)是新增或有修改的,去掉了原來的height,用unStickyDistance來替代。固定時(shí)候相對瀏覽器頂部或底部的位置,用stickyOffset來指定,這樣在.sticky--in-top或.sticky--in-bottom的css里就不用再寫top或bottom屬性值了。isFixedWidth如果為false,才會(huì)去添加resize時(shí)刷新sticky元素寬度的回調(diào):


!opts.isFixedWidth && $win.resize(throttle(function () {
setStickyWidth();
$elem.hasClass(className) && $elem.css('width', stickyWidth);
sticky();
}, opts.wait));

本次實(shí)現(xiàn)相比上次,麻煩的是取消固定時(shí)的邏輯處理,上次sticky元素只有2種狀態(tài),sticky或者unsticky,這次不一樣,sticky狀態(tài)里面又分成了staticSticky和dynamicSticky,前者表示top或bottom值不變的sticky狀態(tài),后者表示top或bottom值會(huì)變化的sticky狀態(tài),其實(shí)后者對應(yīng)的就是快要取消固定的時(shí)候那段范圍,為了更清晰地解決這個(gè)問題,將原來判斷臨界點(diǎn)以及在不同臨界點(diǎn)做不同處理的代碼重構(gòu)成下面這個(gè)樣子:

setSticky = function () {
!$elem.hasClass(className) && $elem.addClass(className).css('width', stickyWidth)
&& (typeof opts.onSticky == 'function' && opts.onSticky($elem, $target));
return true;
},
states = {
staticSticky: function () {
setSticky() && $elem.css(opts.type, opts.stickyOffset);
},
dynamicSticky: function (rect) {
setSticky() && $elem.css(opts.type, rules[opts.type].getDynamicOffset(rect));
},
unSticky: function () {
$elem.hasClass(className) && $elem.removeClass(className).css('width', '').css(opts.type, '')
&& (typeof opts.onUnSticky == 'function' && opts.onUnSticky($elem, $target));
}
},
rules = {
top: {
getState: function (rect) {
if (rect.top < 0 && (rect.bottom - unStickyDistance) > 0) return 'staticSticky';
else if ((rect.bottom - unStickyDistance) <= 0 && rect.bottom > 0) return 'dynamicSticky';
else return 'unSticky';
},
getDynamicOffset: function (rect) {
return -(unStickyDistance - rect.bottom);
}
},
bottom: {
getState: function (rect) {
if (rect.bottom > docClientHeight && (rect.top + unStickyDistance) < docClientHeight) return 'staticSticky';
else if ((rect.top + unStickyDistance) >= docClientHeight && rect.top < docClientHeight) return 'dynamicSticky';
else return 'unSticky';
},
getDynamicOffset: function (rect) {
return -(unStickyDistance + rect.top - docClientHeight);
}
}
}
$win.scroll(throttle(sticky, opts.wait));
function sticky() {
var rect = $target[0].getBoundingClientRect(),
curState = rules[opts.type].getState(rect);
states[curState](rect);
}

有點(diǎn)狀態(tài)模式的思想在里面,不過更簡潔。當(dāng)我寫出這個(gè)代碼的時(shí)候,其實(shí)是很想用之前了解的狀態(tài)機(jī)來寫的,我想過用狀態(tài)機(jī)來寫肯定是可以實(shí)現(xiàn)的,不過為了少引用一個(gè)類庫就算了,等哪天想實(shí)踐狀態(tài)機(jī)的時(shí)候再來嘗試一把。

整體實(shí)現(xiàn)如下:

var Sticky = (function ($) {
function throttle(func, wait) {
var timer = null;
return function () {
var self = this, args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(function () {
return typeof func === 'function' && func.apply(self, args);
}, wait);
}
}
var DEFAULTS = {
target: '', //target元素的jq選擇器
type: 'top', //固定的位置,top | bottom,默認(rèn)為top,表示固定在頂部
wait: 5, //scroll事件回調(diào)的間隔
stickyOffset: 0, //固定時(shí)距離瀏覽器可視區(qū)頂部或底部的偏移,用來設(shè)置top跟bottom屬性的值,默認(rèn)為0
isFixedWidth: true, //sticky元素寬度是否固定,默認(rèn)為true,如果是自適應(yīng)的寬度,需設(shè)置為false
getStickyWidth: undefined, //用來獲取sticky元素寬度的回調(diào),在不傳該參數(shù)的情況下,stickyWidth將設(shè)置為sticky元素的offsetWidth
unStickyDistance: undefined, //該參數(shù)決定sticky元素何時(shí)進(jìn)入dynamicSticky狀態(tài)
onSticky: undefined, ///sticky元素固定時(shí)的回調(diào)
onUnSticky: undefined ///sticky元素取消固定時(shí)的回調(diào)
};
return function (elem, opts) {
var $elem = $(elem);
opts = $.extend({}, DEFAULTS, opts || {}, $elem.data() || {});
var $target = $(opts.target);
if (!$elem.length || !$target.length) return;
var stickyWidth,
setStickyWidth = function () {
stickyWidth = typeof opts.getStickyWidth === 'function' && opts.getStickyWidth($elem) || $elem[0].offsetWidth;
},
docClientHeight = document.documentElement.clientHeight,
unStickyDistance = opts.unStickyDistance || $elem[0].offsetHeight,
setSticky = function () {
!$elem.hasClass(className) && $elem.addClass(className).css('width', stickyWidth)
&& (typeof opts.onSticky == 'function' && opts.onSticky($elem, $target));
return true;
},
states = {
staticSticky: function () {
setSticky() && $elem.css(opts.type, opts.stickyOffset);
},
dynamicSticky: function (rect) {
setSticky() && $elem.css(opts.type, rules[opts.type].getDynamicOffset(rect));
},
unSticky: function () {
$elem.hasClass(className) && $elem.removeClass(className).css('width', '').css(opts.type, '')
&& (typeof opts.onUnSticky == 'function' && opts.onUnSticky($elem, $target));
}
},
rules = {
top: {
getState: function (rect) {
if (rect.top < 0 && (rect.bottom - unStickyDistance) > 0) return 'staticSticky';
else if ((rect.bottom - unStickyDistance) <= 0 && rect.bottom > 0) return 'dynamicSticky';
else return 'unSticky';
},
getDynamicOffset: function (rect) {
return -(unStickyDistance - rect.bottom);
}
},
bottom: {
getState: function (rect) {
if (rect.bottom > docClientHeight && (rect.top + unStickyDistance) < docClientHeight) return 'staticSticky';
else if ((rect.top + unStickyDistance) >= docClientHeight && rect.top < docClientHeight) return 'dynamicSticky';
else return 'unSticky';
},
getDynamicOffset: function (rect) {
return -(unStickyDistance + rect.top - docClientHeight);
}
}
},
className = 'sticky--in-' + opts.type,
$win = $(window);
setStickyWidth();
$win.scroll(throttle(sticky, opts.wait));
!opts.isFixedWidth && $win.resize(throttle(function () {
setStickyWidth();
$elem.hasClass(className) && $elem.css('width', stickyWidth);
sticky();
}, opts.wait));
$win.resize(throttle(function () {
docClientHeight = document.documentElement.clientHeight;
}, opts.wait));
function sticky() {
var rect = $target[0].getBoundingClientRect(),
curState = rules[opts.type].getState(rect);
states[curState](rect);
}
}
})(jQuery);

難理解的可能是getState的那個(gè)方法的邏輯,這部分的一些思路在上上篇博客有比較詳細(xì)的說明。

3. 博客側(cè)邊欄應(yīng)用說明

首先得把本次的實(shí)現(xiàn)粘貼到博客設(shè)置頁腳html文本域里面去,然后加入下面的代碼來初始化:

var timer = setInterval(function(){
if($('#blogCalendar').length && $('#profile_block').length && $('#sidebar_search').length) {
new Sticky('#sideBar', {
target: '#main',
onSticky: function($elem, $target){
$target.css('min-height',$elem.outerHeight());
$elem.css('left', '65px');
},
onUnSticky: function($elem, $target){
$target.css('min-height','');
$elem.css('left', '');
}
});
}
},100);

使用timer是因?yàn)閭?cè)邊欄的內(nèi)容都是ajax加載,又不可能在這些ajax請求時(shí)候添加回調(diào),只能通過它們返回的內(nèi)容來判斷側(cè)邊欄是否加載完畢。

4. 總結(jié)

這周末琢磨了下如何改進(jìn)sticky組件,加上寫這篇文章,花了大半天的時(shí)間,好歹現(xiàn)在這個(gè)sticky組件的功能跟實(shí)現(xiàn)能讓自己有點(diǎn)滿意的感覺了,上次寫完總覺得怪怪的,好像缺點(diǎn)什么,原來是因?yàn)檫€差這么多東西。現(xiàn)在這個(gè)組件還只是能實(shí)現(xiàn)固定和取消固定的效果,對于實(shí)際工作而言,這個(gè)層級(jí)的效果可能還不夠,網(wǎng)上常見的那種在固定的同時(shí)支持導(dǎo)航滾動(dòng)或者tab導(dǎo)航的功能也很常見,下篇文章會(huì)介紹基于本文的sticky組件,如何實(shí)現(xiàn)navScrollSticky以及tabSticky組件,敬請關(guān)注。
感謝您的閱讀:)

補(bǔ)充說明:
IE跟火狐里面,在刷新頁面的時(shí)候,如果刷新前頁面有滾動(dòng),刷新的操作雖然還會(huì)把頁面的滾動(dòng)位置設(shè)置成刷新的位置,但是不會(huì)觸發(fā)scroll事件,所以必須在組件初始化之后立即調(diào)用一次sticky函數(shù):

相關(guān)文章

  • Javascript的console['''']常用輸入方法匯總

    Javascript的console['''']常用輸入方法匯總

    本文給大家?guī)砹耸畮追NJavascript的console['']常用輸入方法,每種方法給大家介紹的都很詳細(xì),需要的朋友參考下吧
    2018-04-04
  • 基于js對象,操作屬性、方法詳解

    基于js對象,操作屬性、方法詳解

    下面小編就為大家?guī)硪黄趈s對象,操作屬性、方法詳解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-08-08
  • JavaScript 私有成員分析

    JavaScript 私有成員分析

    JavaScript是世界上最受誤解的語言。有人認(rèn)為它缺少信息隱藏的屬性(封裝),因?yàn)镴avaScript對象不能擁有私有變量和函數(shù)。但這是誤解。JavaScript對象可以有私有成員。下面進(jìn)行說明。
    2009-01-01
  • 微信小程序?qū)崿F(xiàn)裁剪圖片大小

    微信小程序?qū)崿F(xiàn)裁剪圖片大小

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)裁剪圖片大小,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • JS+CSS實(shí)現(xiàn)的簡單折疊展開多級(jí)菜單效果

    JS+CSS實(shí)現(xiàn)的簡單折疊展開多級(jí)菜單效果

    這篇文章主要介紹了JS+CSS實(shí)現(xiàn)的簡單折疊展開多級(jí)菜單效果,涉及JavaScript頁面元素的遍歷及動(dòng)態(tài)操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • LayUI—tree樹形結(jié)構(gòu)的使用解析

    LayUI—tree樹形結(jié)構(gòu)的使用解析

    這篇文章主要介紹了LayUI—tree樹形結(jié)構(gòu)的使用,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • js獲取對象,數(shù)組所有屬性鍵值(key)和對應(yīng)值(value)的方法示例

    js獲取對象,數(shù)組所有屬性鍵值(key)和對應(yīng)值(value)的方法示例

    這篇文章主要介紹了js獲取對象,數(shù)組所有屬性鍵值(key)和對應(yīng)值(value)的方法,涉及javascript對于對象、數(shù)組鍵名與鍵值遍歷相關(guān)操作技巧,需要的朋友可以參考下
    2019-06-06
  • js使用文件流下載csv文件的實(shí)現(xiàn)方法

    js使用文件流下載csv文件的實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于js使用文件流下載csv文件的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用js具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • Javascript驗(yàn)證上傳圖片大小[前臺(tái)處理]

    Javascript驗(yàn)證上傳圖片大小[前臺(tái)處理]

    在做上傳圖片的時(shí)候,如果不限制上傳圖片大小,后果非常的嚴(yán)重。解決這個(gè)問題有兩種方式:后臺(tái)處理、前臺(tái)處理
    2014-07-07
  • JS實(shí)現(xiàn)的簡單輪播圖運(yùn)動(dòng)效果示例

    JS實(shí)現(xiàn)的簡單輪播圖運(yùn)動(dòng)效果示例

    這篇文章主要介紹了JS實(shí)現(xiàn)的簡單輪播圖運(yùn)動(dòng)效果,結(jié)合完整實(shí)例形式分析了javascript基于定時(shí)器動(dòng)態(tài)修改頁面元素屬性的相關(guān)操作技巧,需要的朋友可以參考下
    2016-12-12

最新評論