stickUp插件
用於實現固定菜單欄效果,原理很簡單,說白了就是監聽document的scroll事件,滾動到特定值時,將特定元素的position設置為fixed,核心代碼如下:

1 $(document).on('scroll', function() { 2 varscroll = parseInt($(document).scrollTop()); 3 if (menuSize != null) { 4 for (var i = 0; i < menuSize; i++) { 5 contentTop[i] = $('#' + content[i] + '').offset().top; 6 7 function bottomView(i) { 8 contentView = $('#' + content[i] + '').height() * .4; 9 testView = contentTop[i] - contentView; 10 //console.log(varscroll); 11 if (varscroll > testView) { 12 $('.' + itemClass).removeClass(itemHover); 13 $('.' + itemClass + ':eq(' + i + ')').addClass(itemHover); 14 } else if (varscroll < 50) { 15 $('.' + itemClass).removeClass(itemHover); 16 $('.' + itemClass + ':eq(0)').addClass(itemHover); 17 } 18 } 19 if (scrollDir == 'down' && varscroll > contentTop[i] - 50 && varscroll < contentTop[i] + 50) { 20 $('.' + itemClass).removeClass(itemHover); 21 $('.' + itemClass + ':eq(' + i + ')').addClass(itemHover); 22 } 23 if (scrollDir == 'up') { 24 bottomView(i); 25 } 26 } 27 } 28 29 30 31 if (vartop < varscroll + topMargin) { 32 $('.stuckMenu').addClass('isStuck'); 33 $('.stuckMenu').next().closest('div').css({ 34 'margin-top': stickyHeight + stickyMarginB + currentMarginT + 'px' 35 }, 10); 36 $('.stuckMenu').css("position", "fixed"); 37 $('.isStuck').css({ 38 top: '0px' 39 }, 10, function() { 40 41 }); 42 }; 43 44 if (varscroll + topMargin < vartop) { 45 $('.stuckMenu').removeClass('isStuck'); 46 $('.stuckMenu').next().closest('div').css({ 47 'margin-top': currentMarginT + 'px' 48 }, 10); 49 $('.stuckMenu').css("position", "relative"); 50 }; 51 52 });
但是,在實際使用過程中,還是發現諸多不便,
- 它只支持最后一次調用(因為是使用閉包實現的變量存儲,但每次init都基於同樣一套變量)
- 存在一些未經聲明的變量調用(即全局變量,這可是很不好的編程習慣啊)
- 存在一些不必要的函數聲明,導致一些不必要的性能損耗(比如上述代碼中的bottomView函數)
- 不支持回調函數,無法支持比較復雜的應用
- 單頁網站時,在頁面滾動到parts參數指定的塊時,會給對應的菜單塊加itemHover類(請參考http://lirancohen.github.io/stickUp/),parts是一個額外指定的參數,而parts中每一個id對應的菜單項又是基於parts參數的順序的,這是一種不穩定結構(語文老師死得早,湊合着看吧
stickUp原項目在github中很久沒有更新了,所以我決定fork出一個分支,然后自己重構stickUp插件,我的項目地址是:https://github.com/VanMess/stickUp,有興趣的童鞋可以clone下來看看,核心文件的代碼只有150多行,結構也比較清晰,大家可以看看,有什么問題請聯系我,交流交流。。。。當然,如果哪位大神能提出一些意見就更好了。
新的stickUp代碼主要分三個部分:Context類、Context._init_ 初始化函數、Context.onScroll 滾動處理函數。
Context是一個上下文數據結構,用於記錄每次調用的上下文信息,這樣就解決了上面的第1個問題,代碼如下:

1 var Context = function() {}, 2 _ctxList = {}, 3 lastScrollTop = 0; 4 Context.prototype = { 5 dataProperty: 'data-menu', 6 selector: '', 7 itemClass: '', 8 itemHover: '', 9 jqDom: null, 10 menuItems: [], 11 region: 'top', 12 height: 0, 13 parentMarginTop: 0, 14 top: 0, 15 marginTop: 0, 16 marginBottom: 0, 17 beforeStick: null, 18 afterStick: null, 19 beforeUnstick: null, 20 afterUnstick: null 21 };
具體每一項的含義、用法,建議大家可以看看源碼。
Context._init_ 是一個初始化函數,一個工廠,接受一個option參數,並將之轉換為一個Context實例,注意,這里使用了_ctxList 變量來存儲歷史以來所有的上下文信息,代碼如下:

最后,是Context.prototype.onScroll 類,用於處理頁面滾動事件,是整個stickUp的核心所在,代碼如下:

onScroll: function(scrollDir, varscroll) { var contentView = null, testView = null, _me = this; // 計算並給適當元素添加 itemHover 類 if ( !! _me.menuItems && _me.menuItems.length > 0) { var offset = null, contentTop = 0, tmp_menuTarget = null; for (var i = 0; i < _me.menuItems.length; i++) { tmp_menuTarget = $('#' + $(_me.menuItems[i]).attr(_me.dataProperty)); offset = tmp_menuTarget.offset(); contentTop = !! offset ? offset.top : 0; // 之前這裡定義了一個bottomView // 會在每次執行這個地方的時候都去創建一個函數 // 實際上是很沒必要的性能損耗,所以這里將代碼移動下面 if (scrollDir == 'down' && varscroll > contentTop - 50 && varscroll < contentTop + 50) { _me.jqDom.find('.' + _me.itemClass).removeClass(_me.itemHover); _me.jqDom.find('.' + _me.itemClass + ':eq(' + i + ')').addClass(_me.itemHover); } if (scrollDir == 'up') { // 這里就是原來的bottomView代碼 contentView = tmp_menuTarget.height() * 0.4; testView = contentTop - contentView; if (varscroll > testView) { _me.jqDom.find('.' + _me.itemClass).removeClass(_me.itemHover); _me.jqDom.find('.' + _me.itemClass + ':eq(' + i + ')').addClass(_me.itemHover); } else if (varscroll < 50) { _me.jqDom.find('.' + _me.itemClass).removeClass(_me.itemHover); _me.jqDom.find('.' + _me.itemClass + ':eq(0)').addClass(_me.itemHover); } } } } // 固定菜單欄目,使之固定(fixed) if (_me.top < varscroll + _me.marginTop) { if ( !! _me.beforeStick) _me.beforeStick.call(_me); _me.jqDom.addClass('isStuck'); if ( !! _me.afterStick) _me.afterStick.call(_me); _me.jqDom.next().closest('div').css({ 'margin-top': _me.height + _me.marginBottom + _me.parentMarginTop + 'px' }, 10); _me.jqDom.css("position", "fixed"); _me.jqDom.css({ top: '0px' }, 10); }; // 菜單欄目,使之不固定(relative) if (varscroll + _me.marginTop < _me.top) { if ( !! _me.beforeUnstick) _me.beforeUnstick.call(_me); _me.jqDom.removeClass('isStuck'); if ( !! _me.afterUnstick) _me.afterUnstick.call(_me); _me.jqDom.next().closest('div').css({ 'margin-top': _me.parentMarginTop + 'px' }, 10); _me.jqDom.css("position", "relative"); }; }
后記:一直想做一個自己的開源項目,不過還沒想清楚要做什么,所以想着先拿別人的來重構、優化,這次使用stickUp是看中它的小(下次想修改百度的ECharts)。stickUp還不是很成熟,但根據我的測試在多個瀏覽器下都不存在大問題,歡迎大家使用,有什么問題請盡管聯系我
另外,看在我辛苦的份上,就麻煩大家留個言鼓勵鼓勵吧,謝謝
最后附上源碼:https://github.com/VanMess/stickUp
最后附上源碼:https://github.com/VanMess/stickUp