最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

1. 舊版本的問(wèn)題

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

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

它就把固定的時(shí)候,相對(duì)瀏覽器頂部的位置設(shè)置成了top: 20px。sticky元素的高度也是,為了在固定的時(shí)候顯示更好看的效果,調(diào)整原來(lái)的Line-height或者padding-top等更高度有關(guān)的屬性,也是非常常見(jiàn)的需求,比如天貓花唄的這個(gè)頁(yè)面,這塊內(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)頁(yè)主體內(nèi)容一起向上滾動(dòng):

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

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

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

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

4)在sticky元素固定和取消固定的時(shí)候,應(yīng)該提供回調(diào)函數(shù),以便其它組件依賴(lài)這個(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ū)頂部或底部的偏移,用來(lái)設(shè)置top跟bottom屬性的值,默認(rèn)為0
isFixedWidth: true, //sticky元素寬度是否固定,默認(rèn)為true,如果是自適應(yīng)的寬度,需設(shè)置為false
getStickyWidth: undefined, //用來(lái)獲取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è)是新增或有修改的,去掉了原來(lái)的height,用unStickyDistance來(lái)替代。固定時(shí)候相對(duì)瀏覽器頂部或底部的位置,用stickyOffset來(lái)指定,這樣在.sticky--in-top或.sticky--in-bottom的css里就不用再寫(xiě)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í)后者對(duì)應(yīng)的就是快要取消固定的時(shí)候那段范圍,為了更清晰地解決這個(gè)問(wèn)題,將原來(lái)判斷臨界點(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)模式的思想在里面,不過(guò)更簡(jiǎn)潔。當(dāng)我寫(xiě)出這個(gè)代碼的時(shí)候,其實(shí)是很想用之前了解的狀態(tài)機(jī)來(lái)寫(xiě)的,我想過(guò)用狀態(tài)機(jī)來(lái)寫(xiě)肯定是可以實(shí)現(xiàn)的,不過(guò)為了少引用一個(gè)類(lèi)庫(kù)就算了,等哪天想實(shí)踐狀態(tài)機(jī)的時(shí)候再來(lái)嘗試一把。

整體實(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ū)頂部或底部的偏移,用來(lái)設(shè)置top跟bottom屬性的值,默認(rèn)為0
isFixedWidth: true, //sticky元素寬度是否固定,默認(rèn)為true,如果是自適應(yīng)的寬度,需設(shè)置為false
getStickyWidth: undefined, //用來(lái)獲取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ì)的說(shuō)明。

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

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

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請(qǐng)求時(shí)候添加回調(diào),只能通過(guò)它們返回的內(nèi)容來(lái)判斷側(cè)邊欄是否加載完畢。

4. 總結(jié)

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

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

相關(guān)文章

  • ES6學(xué)習(xí)教程之Map的常用方法總結(jié)

    ES6學(xué)習(xí)教程之Map的常用方法總結(jié)

    Map 是 ES6 中新增的一種數(shù)據(jù)結(jié)構(gòu),與 Set 一起添加,其實(shí)功能都差不多。下面這篇文章主要給大家總結(jié)介紹了關(guān)于ES6學(xué)習(xí)教程之Map的常用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-08-08
  • JavaScript聲明函數(shù)的5種方法小結(jié)

    JavaScript聲明函數(shù)的5種方法小結(jié)

    本文主要介紹了JavaScript聲明函數(shù)的5種方法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • JavaScript 垃圾回收機(jī)制分析

    JavaScript 垃圾回收機(jī)制分析

    同C# 、Java一樣我們可以手工調(diào)用垃圾回收程序,但是由于其消耗大量資源,而且我們手工調(diào)用的不會(huì)比瀏覽器判斷的準(zhǔn)確,所以不推薦手工調(diào)用垃圾回收
    2013-10-10
  • 深入理解JavaScript系列(6) 強(qiáng)大的原型和原型鏈

    深入理解JavaScript系列(6) 強(qiáng)大的原型和原型鏈

    JavaScript 不包含傳統(tǒng)的類(lèi)繼承模型,而是使用 prototypal 原型模型
    2012-01-01
  • JavaScript中boolean類(lèi)型之三種情景實(shí)例代碼

    JavaScript中boolean類(lèi)型之三種情景實(shí)例代碼

    下面小編就為大家?guī)?lái)一篇JavaScript中boolean類(lèi)型之三種情景實(shí)例代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-11-11
  • 使用js獲取地址欄參數(shù)的方法推薦(超級(jí)簡(jiǎn)單)

    使用js獲取地址欄參數(shù)的方法推薦(超級(jí)簡(jiǎn)單)

    下面小編就為大家?guī)?lái)一篇使用js獲取地址欄參數(shù)的方法推薦(超級(jí)簡(jiǎn)單)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • 以fetch為例詳解如何消除js異步的傳染性

    以fetch為例詳解如何消除js異步的傳染性

    這篇文章主要為大家介紹了以fetch為例詳解如何消除js異步的傳染性示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • JS去掉字符串前后空格、阻止表單提交的實(shí)現(xiàn)代碼

    JS去掉字符串前后空格、阻止表單提交的實(shí)現(xiàn)代碼

    這篇文章主要介紹了JS去掉字符串前后空格、阻止表單提交的實(shí)現(xiàn)代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-06-06
  • js removeChild 方法深入理解

    js removeChild 方法深入理解

    下面小編就為大家?guī)?lái)一篇js removeChild 方法深入理解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • 微信小程序?qū)崿F(xiàn)拼圖小游戲

    微信小程序?qū)崿F(xiàn)拼圖小游戲

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)拼圖小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-10-10

最新評(píng)論

临汾市| 苏尼特右旗| 射阳县| 塘沽区| 息烽县| 左权县| 介休市| 南郑县| 资阳市| 精河县| 三门县| 南安市| 岚皋县| 霍州市| 蒲江县| 若羌县| 郁南县| 南平市| 巨野县| 昌平区| 沂南县| 如皋市| 永定县| 昌宁县| 徐州市| 特克斯县| 宜黄县| 长丰县| 大厂| 新化县| 扬中市| 九龙县| 乐清市| 威宁| 翁源县| 开原市| 台南市| 甘谷县| 肃北| 汪清县| 乐业县|