jQuery.stickUp插件重構


  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             });
View Code

 

  但是,在實際使用過程中,還是發現諸多不便,
  • 它只支持最后一次調用(因為是使用閉包實現的變量存儲,但每次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 };
View Code

 

  具體每一項的含義、用法,建議大家可以看看源碼。
    Context._init_ 是一個初始化函數,一個工廠,接受一個option參數,並將之轉換為一個Context實例,注意,這里使用了_ctxList 變量來存儲歷史以來所有的上下文信息,代碼如下:
 
View Code

 

  最后,是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");
            };
        }
View Code

 

后記:一直想做一個自己的開源項目,不過還沒想清楚要做什么,所以想着先拿別人的來重構、優化,這次使用stickUp是看中它的小(下次想修改百度的ECharts)。stickUp還不是很成熟,但根據我的測試在多個瀏覽器下都不存在大問題,歡迎大家使用,有什么問題請盡管聯系我
另外,看在我辛苦的份上,就麻煩大家留個言鼓勵鼓勵吧,謝謝
最后附上源碼:https://github.com/VanMess/stickUp
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM