改良版 导航栏自动跟随

 

简约版2.0 无横向自动跟随

dom结构为  

导航条: .nav-box      //样式 : 宽高与.nav 一致, 相对定位 position:relative;
      .nav //样式 : display:flex;弹性盒子布局, 对li进行布局 , 设置宽高 ,设置层级z-index:7 避免被其他层级高的元素遮挡
        li.active //.active 为选中后的样式
        li
        li
内容:
  .main-cont //每个li对应的内容

  .main-cont
  .main-cont


js内容
 function navFixed(params) {
            // 点击导航栏变化颜色
            $(params.navParent).on('click', params.nav, function (e) {
                //停止监听页面scroll
                params.flag = 1;
                $(e.target).addClass(params.active).siblings('a').removeClass(params.active);
                var nowIndex=$(e.target).index();
                $(params.navParent).addClass(params.navActive);
                //点击后页面滚动到指定位置
                $('html,body').animate({
                    scrollTop: $(params.itemAll).eq(nowIndex).offset().top - $(params.navParent).outerHeight()
                }, 500, function () {
                    setTimeout(function () {
                        params.flag = 0;
                    }, 100)
                });
            })
            //设置简单节流
            var timer;
            //获取要滚动元素的高度
            var arrHeight = [];
            $(params.itemAll).each(function (i, v) {
                arrHeight.push($(v).offset().top);
            })
            //获取导航栏父元素相对顶部的高度与scroll比较来判断导航栏是否跟随
            var navHeight = $(params.navParent).offset().top;
            $(window).scroll(function () {
                if (params.flag) {
                    return;
                }
                var scrollTop = $(document).scrollTop();
                var windowHeight = $(this).height();
                var scrollHeight = $(document).height();
                
                //导航栏跟随
                if (scrollTop < navHeight) {
                    $(params.navParent).removeClass('active');
                    return;
                }
                if (timer) {
                    clearTimeout(timer);
                }
                timer = setTimeout(function () {
                    $(params.navParent).addClass('active');
                    timer = undefined;
                }, 50);
                //根据页面高度改变导航栏背景色
                arrHeight.forEach(function (v, i) {
                    if (scrollTop >= v - params.deviation) {
                        $(params.navParent).find(params.nav).eq(i).addClass(params.active).siblings(params.nav).removeClass(params.active);
                    }else if(scrollTop + windowHeight == scrollHeight){// 判断滚动到最底部
                        // alert("已经到最底部了!");
                    $(".nav-box a:last").addClass('active').siblings().removeClass('active')
                    }
                })
            })
        }
            navFixed({
                navParent: '.nav-box',  
                nav: 'a',
                navItem: '.nav-box>a',
                navActive:'active',
                itemParent: '.main-cont',
                itemAll:'.main-cont>div',
                active: 'active',
                fixedTop: '0',
                deviation: $('.nav-main').outerHeight(),
                flag: 0
            });

导航栏自动跟随部分

css部分:

           .container {
                /*最外层div定宽*/
                position: relative;
                width: 15rem;
                height: 1rem;
                margin-top: 20rem;
            }        
            .con {
                /*第二层div,设置溢出为滚动条*/
                overflow-x: scroll;
                overflow-y: hidden;
                height: 1rem;
                font-size: 0.4rem;
                width: 15rem;
                position: absolute;
                top: 0;
            }        
            .nav {
                /*导航条div 设置弹性盒子,使得子集为一行显示,设置子集float也可以       宽度为js动态设置*/
                display: flex;
            }            
            .nav li {
                /*子集样式*/
                width: 4rem;
                flex: 0 0 4rem;
                border: 1px solid #000000;
            }
            .active {
                /*选中后的样式*/
                background-color: bisque;
            }
            
            /*以下是监听元素的样式*/
            .list {
                margin-bottom: 30rem;
            }
            .item {
                height: 6rem;
                width: 15rem;
                border: 1px solid;
            }                    

html结构部分:

注意:  导航栏 html结构为三层 , 第一层为给导航栏占位置的,    第二层给导航栏提供 滚动条  , 第三层 是导航子集的容器

    <!--导航栏部分-->
        <div class="container">
            <div class="con">
                <ul class="nav">
                    <li class="active"> 1111111</li>
                    <li>222222</li>
                    <li>333333</li>
                    <li>44444</li>
                    <li>55555555</li>
                    <li>66666666</li>
                    <li>7777</li>
                </ul>
            </div>
        </div>
            
        <!--监听元素部分-->    
        <div class="list">
            <div class="item">
                111111111111111111
            </div>
            <div class="item">
                222222222222222222
            </div>
            <div class="item">
                3333333333333333333
            </div>
            <div class="item">
                444444444444444444
            </div>
            <div class="item">
                5555555555555555555
            </div>
            <div class="item">
                6666666666666666666
            </div>
            <div class="item">
                77777777777777777777
            </div>
        </div>

12月17日js部分

$(function() {
                scrollgen({ //调用  全部可选参数        导航栏默认为    .nav
                    navChild: '.nav li', //导航栏子集 li
                    navFather: '.con', //导航栏父级
                    nav: '.nav', //导航栏ul
                    active: '.active', //被选中后的样式
                    control: '.item', //监听的元素
                    //isFollow: true //默认为false 为全部显示  false为tab切换 //true  为全部显示, 监视滚动条跟随 
                })

                function scrollgen(obj) {

                    //默认参数
                    var objDefault = {
                        navChild: $('.nav').children(), //默认获取子节点 
                        navFather: $('.nav').parent(), //默认获取父级
                        nav: $('.nav'),
                        active: $('.active'),
                        control: $('.nav').parent().parent().next().children(),
                    };
                    if(!obj) {
                        obj = {}
                    }
                    $.extend(obj, objDefault);

                    var $navChild = $(obj.navChild),
                        $navFather = $(obj.navFather),
                        $nav = $(obj.nav),
                        $active = $(obj.active),
                        active = $active[0].className,
                        $control = $(obj.control),
                        isFollow = obj.isFollow || false; //默认为false

                    $navChild.eq(0).addClass(active).siblings().removeClass(active);//初始值为第一个li选中
                    $navFather.animate({
                        scrollLeft: 0
                    }, 200)
                    var nh = $nav.offset().top, //获取导航栏在文档中的高度
                        sh = $(document).scrollTop(); //获取滚轮高度
                    var start_nH = $nav.offset().top
                    index = $navChild.index() + 1
                    //动态设置nav 宽度
                    $nav.width(function() {
                        return $navChild.width() * index + 10 + 'px';
                    })
                    var Harr = [], //存储item高度容器
                        Warr = []; //存储nav li offsetleft容器
                    $control.each(function(index, el) {
                        Harr.push(el.offsetTop);
                    })
                    $navChild.each((index, el) => {
                        Warr.push(el.offsetLeft);
                    })

                    if(isFollow == false) {
                        $control.eq(0).css({
                            'display': 'block'
                        }).siblings().css({
                            'display': 'none'
                        });
                        $navChild.click(function() {
                            var i = $(this).index()
                            $control.eq(i).css({
                                'display': 'block'
                            }).siblings().css({
                                'display': 'none'
                            });
                            $('html,body').animate({
                                scrollTop: $control.eq(i).offset().top - $nav.height() - 10
                            }, 400);
                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                            var item = $("." + active);
                            var itemOffset = item.offset();
                            //元素离    scrollLeft等于0  时的距离
                            var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                            //scrollLeft等于0,居中时的offsetLeft
                            var centerLeft = ($navFather.width() - item.width()) / 2;
                            $navFather.stop().animate({
                                scrollLeft: itemOffsetLeft - centerLeft
                            }, 200)
                        })
                    }
                    $(window).scroll(function() {
                        sh = $(document).scrollTop();
                        if($(window).scrollTop() >= nh) {
                            $navFather.css({
                                'position': 'fixed',
                                "top": '0'
                            });
                            Harr.forEach((n, i) => {
                                if($(window).scrollTop() >= n - $nav.height() - 15) {
                                    if(isFollow) {
                                        $navChild.eq(i).addClass(active).siblings().removeClass(active);
                                    }
                                }
                            });

                            //以下是导航跟随逻辑,
                            //思路:计算出scrollLeft =0 时   元素离文档最左侧的距离   -   滚动后元素离  scrollLeft =0 时 元素离文档最左侧的距离  =滚动距离
                            var item = $("." + active);
                            var itemOffset = item.offset();
                            //元素离    scrollLeft等于0  时的距离
                            var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                            //scrollLeft等于0,居中时的offsetLeft
                            var centerLeft = ($navFather.width() - item.width()) / 2;
                            $navFather.stop().animate({
                                scrollLeft: itemOffsetLeft - centerLeft
                            })
                        } else {
                            $navFather.css({
                                'position': 'absolute',
                                "z-index": '7'
                            })
                        }
                    });

                    $navChild.click(function() {
                        var i = $(this).index()
                        var offsetH = $control.eq(i).offset().top;
                        $('html,body').animate({
                            scrollTop: $control.eq(i).offset().top - $nav.height() - 10
                        }, 500)
                    })
                }
            })

 

 

 

 

12月18日改良后   js部分:

改良后优化:    

1.参数可以为   字符串,数组,对象 ,也可以为空     默认参数优化

2.通过  参数 添加tab切换  

3.滚动条添加防抖,避免滚动频繁触发函数,造成页面滚动卡顿

4.滚动监听元素位置不准确优化

 

 

$(function() {
                //    scrollFollow(
                //    { //调用  全部可选参数        导航栏默认为    .nav
                //                    navChild: '.nav li', //导航栏子集 li
                //                    navFather: '.con', //导航栏父级
                //                    nav: '.nav', //导航栏ul
                //                    active: '.active', //被选中后的样式
                //                    control: '.item', //监听的元素
                //                    //isFollow: true //默认为false false为tab切换    true 为列表全部显示 滚动监听跟随
                //                }
                //)

                scrollFollow(['.nav', '.active', '.item', false])

                function scrollFollow(option) {
                    //根据参数的情况,构造出合适的对象,调用 _scrollFollow
                    if(arguments.length === 0) {
                        //没有参数
                        _scrollFollow({});
                    } else if(arguments.length === 1) {
                        if(arguments[0] instanceof Array) {
                            //有一个参数
                            console.log(123)
                            _scrollFollow({
                                nav: arguments[0][0],
                                active: arguments[0][1],
                                control: arguments[0][2],
                                isFollow: arguments[0][3],

                            })
                        } else
                        if(arguments[0] instanceof Object) {
                            console.log(456)
                            _scrollFollow(arguments[0]); //第一个参数就是对象
                        }
                    } else if(arguments.length === 2) {
                        //参数数量大于等于2,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1]
                        })
                    } else if(arguments.length === 3) {
                        //参数数量大于等于3,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1],
                            control: arguments[2],
                        })
                    } else if(arguments.length === 4) {
                        //参数数量大于等于4,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1],
                            control: arguments[2],
                            isFollow: arguments[3],
                        })
                    }

                }

                function _scrollFollow(obj) {

                    //默认参数
                    var objDefault = {
                        navChild: $('.nav').children(), //默认获取子节点 
                        navFather: $('.nav').parent(), //默认获取父级
                        nav: $('.nav'),
                        active: $('.active'),
                        control: $('.nav').parent().parent().next().children(),
                    };
                    var obj = Object.assign({}, objDefault, obj);
                    var $navChild = $(obj.navChild),
                        $navFather = $(obj.navFather),
                        $nav = $(obj.nav),
                        $active = $(obj.active),
                        active = $active[0].className,
                        $control = $(obj.control),
                        isFollow = obj.isFollow || false; //默认为false

                    $navChild.eq(0).addClass(active).siblings().removeClass(active);
                    $navFather.animate({
                        scrollLeft: 0
                    }, 200)
                    var nh = $nav.offset().top, //获取导航栏在文档中的高度
                        sh = $(document).scrollTop(); //获取滚轮高度
                    var start_nH = $nav.offset().top
                    index = $navChild.index() + 1
                    //动态设置nav 宽度
                    $nav.width(function() {
                        return $navChild.width() * index + 10 + 'px';
                    })
                    var Harr = [], //存储item高度容器
                        Warr = []; //存储nav li offsetleft容器
                    $control.each(function(index, el) {
                        Harr.push(el.offsetTop);
                    })
                    $navChild.each((index, el) => {
                        Warr.push(el.offsetLeft);
                    })

                    let lock = false;
                    $(window).scroll(function() {
                        _throttle(_scroll, 80); //滚动防抖,防止滚动函数触发频繁,造成滚动卡顿
                        function _throttle(func, delay = 60) {
                            return(() => {
                                if(lock) {
                                    return
                                };
                                func();
                                lock = true;
                                setTimeout(() => {
                                    lock = false
                                }, delay)
                            })()
                        }

                        function _scroll() {
                            sh = $(document).scrollTop();
                            if($(window).scrollTop() >= nh) {
                                $navFather.css({
                                    'position': 'fixed',
                                    "top": '0'
                                });
                                Harr.forEach((n, i) => {
                                    if($(window).scrollTop() + $nav.height() >= n - 110) {
                                        //tab切换设置
                                        if(isFollow) {
                                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                                        }
                                    }
                                });

                                //以下是导航跟随逻辑,
                                //思路:计算出scrollLeft =0 时   元素离文档最左侧的距离   -   滚动后元素离  scrollLeft =0 时 元素离文档最左侧的距离  =滚动距离
                                var item = $("." + active);
                                var itemOffset = item.offset();
                                //元素离    scrollLeft等于0  时的距离
                                var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                                //scrollLeft等于0,居中时的offsetLeft
                                var centerLeft = ($navFather.width() - item.width()) / 2;
                                $navFather.stop().animate({
                                    scrollLeft: itemOffsetLeft - centerLeft
                                })
                            } else {
                                $navFather.css({
                                    'position': 'absolute',
                                    "z-index": '7'
                                })
                            }
                        }
                    });
                    if(isFollow == false) {
                        $control.eq(0).css({
                            'display': 'block'
                        }).siblings().css({
                            'display': 'none'
                        });
                        $navChild.click(function() {
                            var i = $(this).index()
                            $control.eq(i).css({
                                'display': 'block'
                            }).siblings().css({
                                'display': 'none'
                            });
                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                            var item = $("." + active);
                            var itemOffset = item.offset();
                            //元素离    scrollLeft等于0  时的距离
                            var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                            //scrollLeft等于0,居中时的offsetLeft
                            var centerLeft = ($navFather.width() - item.width()) / 2;
                            $navFather.stop().animate({
                                scrollLeft: itemOffsetLeft - centerLeft
                            }, 200);
                            $('html,body').animate({
                                scrollTop: $control.eq(i).offset().top - $nav.height() - 10
                            }, 300)
                        })
                    } else {
                        $navChild.click(function() {

                            var i = $(this).index()
                          //$navChild.eq(i).addClass(active).siblings().removeClass(active);

                            var offsetH = $control.eq(i).offset().top;
                            $('html,body').animate({
                                scrollTop: $control.eq(i).offset().top - $nav.height() - 10
                            }, 300)
                        })
                    }

                }
            })

 

 

12月19日   改良后   js部分 ,html部分   ,  css部分 :

1.参数优化 ,  参数数量缩小

2.滚动监听元素位置优化    通过  监听元素距离导航栏距离调整  , 及滚动距离切换active    后续可以扩展为添加参数 调整距离

3.导航栏的辅助父级元素 ,改为动态添加,避免造成影响页面内其nav父级的样式问题

4.导航栏动态添加宽度,改为手动设置宽度, 避免导航栏动态添加的样式将手动设置的样式影响

 

 

css部分:

.nav {
                /*导航条div 设置弹性盒子,使得子集为一行显示,设置子集float也可以       宽度为js动态设置*/
                display: flex;
                width: 100%;
            }            
            .nav li {
                /*子集样式*/
                width: 4rem;
                flex: 1;
                border: 1px solid #000000;
                height: 1rem;                                    
                font-size: 0.4rem;    
                width: 50%;
            }
            
            .active {
                /*选中后的样式*/
                background-color: bisque;
            }
            /*以下是监听元素的样式*/
            
            .list {
                margin-bottom: 30rem;
            }
            
            .item {
                height: 6rem;
                width: 15rem;
                border: 1px solid;
            }

html部分:

<ul class="nav">
            <li class="active"> 1111111</li>
            <li>222222</li>
            <!--<li>333333</li>
            <li>44444</li>
            <li>55555555</li>
            <li>66666666</li>
            <li>7777</li>-->
        </ul>

        <!--监听元素部分-->
        <div class="list">
            <div class="item">
                111111111111111111
            </div>
            <div class="item">
                222222222222222222
            </div>
                           <!--注释测试tab -->
            <!--<div class="item">
                3333333333333333333
            </div>
            <div class="item">
                444444444444444444
            </div>
            <div class="item">
                5555555555555555555
            </div>
            <div class="item">
                6666666666666666666
            </div>
            <div class="item">
                77777777777777777777
            </div>-->
        </div>            

js部分:

$(function() {
                scrollFollow(['.nav1', '.active', '.item', false])  //参数必填   导航栏类名, 选中类名 , 监听元素类名 , 是否为  非tab 
                function scrollFollow(option) {
                    //根据参数的情况,构造出合适的对象,调用 _scrollFollow
                    if(arguments.length === 0) {
                        //没有参数
                        _scrollFollow({});
                    } else if(arguments.length === 1) {
                        if(arguments[0] instanceof Array) {
                            //有一个参数
                            console.log(123)
                            _scrollFollow({
                                nav: arguments[0][0],
                                active: arguments[0][1],
                                control: arguments[0][2],
                                isFollow: arguments[0][3],

                            })
                        } else
                        if(arguments[0] instanceof Object) {
                            console.log(456)
                            _scrollFollow(arguments[0]); //第一个参数就是对象
                        }
                    } else if(arguments.length === 2) {
                        //参数数量大于等于2,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1]
                        })
                    } else if(arguments.length === 3) {
                        //参数数量大于等于3,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1],
                            control: arguments[2],
                        })
                    } else if(arguments.length === 4) {
                        //参数数量大于等于4,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1],
                            control: arguments[2],
                            isFollow: arguments[3],
                        })
                    }

                }

                function _scrollFollow(obj) {                                        
                    //默认参数
                    var objDefault = {
                        nav: $('.nav'),
                        active: $('.active'),
                        control: $('.nav').parent().parent().next().children(),
                    };
                    var obj = Object.assign({}, objDefault, obj);

                    var    $nav = $(obj.nav),          //nav   ul
                        $navChild =$nav.children(),  //nav子集     li                
                        $active = $(obj.active),     //选中
                        active = $active[0].className,  //获取选中的class名
                        $control = $(obj.control),    //监听的元素  item
                        isFollow = obj.isFollow || false, //默认为false
                        nav_MarginBottom = 10,    //滚动时    nav 距离监听元素的距离  
                        control_top = $control.height() * 0.2;    //nav离  滚动距离到下一个元素 还有多远   
                                                                    //当前的$control离nav的距离超过20%,就切换active
                    //动态添加父级
                    $nav.wrap("<div></div>");
                    $nav.parent().addClass('navFather');
                    
                    $('.navFather').css({
                        'overflow-x': 'auto',
                        'overflow-y': 'hidden',
                        'width': '100%',
                        'position': 'absolute',
                        'top': 0,
                    })
                    $('.navFather').wrap("<div></div>");
                    $('.navFather').parent().addClass('grandFather');    
                    $('.grandFather').css({
                        'position': 'relative',
                        'height':$('.navFather').height(),                        
                    })
                    
                    var  $navFather =$('.navFather');  //nav父级
                    
                    $navChild.eq(0).addClass(active).siblings().removeClass(active);//初始化
                    $navFather.animate({
                        scrollLeft: 0
                    }, 200)
                    var nh = $nav.offset().top, //获取导航栏在文档中的高度
                        sh = $(document).scrollTop(); //获取滚轮高度
                    var start_nH = $nav.offset().top
                    index = $navChild.index() + 1
                    //动态设置nav 宽度  改为  手动添加
                    
//                    $nav.width(function() {
//                        return $navChild.width() * index +'px';
//                    })
                    
                    var Harr = [], //存储item高度容器
                        Warr = []; //存储nav li offsetleft容器
                    $control.each(function(index, el) {
                        Harr.push(el.offsetTop);
                    })
//                    $navChild.each((index, el) => {
//                        Warr.push(el.offsetLeft);
//                    })

                    let lock = false;  //滚动防抖锁
                    $(window).scroll(function() {
                        function _throttle(func, delay = 60) {
                            return(() => {
                                if(lock) {
                                    return
                                };
                                func();
                                lock = true;
                                setTimeout(() => {
                                    lock = false
                                }, delay)
                            })()
                        }
                        _throttle(_scroll, 80); //滚动防抖,防止滚动函数触发频繁,造成滚动卡顿                        
                        function _scroll() {  //滚动触发的函数
                            sh = $(document).scrollTop();
                            if($(window).scrollTop() >= nh) {
                                $navFather.css({
                                    'position': 'fixed',
                                    "top": '0'
                                });
                                Harr.forEach((n, i) => {
                                    if($(window).scrollTop() + $nav.height() >= n -  control_top) {  //
                                        //tab切换设置
                                        if(isFollow) {
                                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                                        }
                                    }
                                });

                                //以下是导航跟随逻辑,
                                //思路:计算出scrollLeft =0 时   元素离文档最左侧的距离   -   滚动后元素离  scrollLeft =0 时 元素离文档最左侧的距离  =滚动距离
                                var item = $("." + active);
                                var itemOffset = item.offset();
                                //元素离    scrollLeft等于0  时的距离
                                var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                                //scrollLeft等于0,居中时的offsetLeft
                                var centerLeft = ($navFather.width() - item.width()) / 2;
                                $navFather.stop().animate({
                                    scrollLeft: itemOffsetLeft - centerLeft
                                })
                            } else {
                                $navFather.css({
                                    'position': 'absolute',
                                    "z-index": '7'
                                })
                            }
                        }
                    });
                    if(isFollow == false) {
                        $control.eq(0).css({
                            'display': 'block'
                        }).siblings().css({
                            'display': 'none'
                        });
                        $navChild.click(function() {
                            var i = $(this).index()
                            $control.eq(i).css({
                                'display': 'block'
                            }).siblings().css({
                                'display': 'none'
                            });
                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                            var item = $("." + active);
                            var itemOffset = item.offset();
                            //元素离    scrollLeft等于0  时的距离
                            var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                            //scrollLeft等于0,居中时的offsetLeft
                            var centerLeft = ($navFather.width() - item.width()) / 2;
                            $navFather.stop().animate({
                                scrollLeft: itemOffsetLeft - centerLeft
                            }, 200);
                            $('html,body').animate({
                                scrollTop: $control.eq(i).offset().top - $nav.height()
                            }, 300)
                        })
                    } else {
                        $navChild.click(function() {
                            var i = $(this).index()
                                //点击改变active 改为 滚动监听改变active 
//                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                            var offsetH = $control.eq(i).offset().top;
                            $('html,body').animate({
                                scrollTop: $control.eq(i).offset().top - $nav.height() - nav_MarginBottom
                            }, 300)
                        })
                    }

                }
            })

 

 12  28日   

JS部分改良:

避免导航栏子集为浮动元素,造成高度坍塌,所以 ,将  js 动态添加上的 navFather   和  grandFather    加上height:100%   ,  

加上后会造成元素为  navFather  样式为  position  :‘fixed‘    时充满全屏   ,所以加上条件为   $('.navFather').height()

 

$(function() {
                scrollFollow(['.nav ul', '.nav .active', '.ui-sortable', false])  //参数必填   导航栏类名, 选中类名 , 监听元素类名 , 是否为  非tab 
                function scrollFollow(option) {
                    //根据参数的情况,构造出合适的对象,调用 _scrollFollow
                    if(arguments.length === 0) {
                        //没有参数
                        _scrollFollow({});
                    } else if(arguments.length === 1) {
                        if(arguments[0] instanceof Array) {
                            //有一个参数
                            console.log(123)
                            _scrollFollow({
                                nav: arguments[0][0],
                                active: arguments[0][1],
                                control: arguments[0][2],
                                isFollow: arguments[0][3],

                            })
                        } else
                        if(arguments[0] instanceof Object) {
                            console.log(456)
                            _scrollFollow(arguments[0]); //第一个参数就是对象
                        }
                    } else if(arguments.length === 2) {
                        //参数数量大于等于2,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1]
                        })
                    } else if(arguments.length === 3) {
                        //参数数量大于等于3,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1],
                            control: arguments[2],
                        })
                    } else if(arguments.length === 4) {
                        //参数数量大于等于4,这种情况只需要前两个参数即可
                        _scrollFollow({
                            nav: arguments[0],
                            active: arguments[1],
                            control: arguments[2],
                            isFollow: arguments[3],
                        })
                    }

                }

                function _scrollFollow(obj) {                                        
                    //默认参数
                    var objDefault = {
                        nav: $('.nav'),
                        active: $('.active'),
                        control: $('.nav').parent().parent().next().children(),
                    };
                    var obj = Object.assign({}, objDefault, obj);

                    var    $nav = $(obj.nav),          //nav   ul
                        $navChild =$nav.children(),  //nav子集     li                
                        $active = $(obj.active),     //选中
                        active = $active[0].className,  //获取选中的class名
                        $control = $(obj.control),    //监听的元素  item
                        isFollow = obj.isFollow || false, //默认为false
                        nav_MarginBottom = 10,    //滚动时    nav 距离监听元素的距离  
                        control_top = $control.height() * 0.2;    //nav离  滚动距离到下一个元素 还有多远   
                                                                    //当前的$control离nav的距离超过20%,就切换active
                    //动态添加父级


   if($nav.parent(). attr("class") != 'navFather') {
      $nav.wrap("<div></div>");
      $nav.parent().addClass('navFather');
      $('.navFather').wrap("<div></div>");
   }
    $('.navFather').css({
       'overflow-x': 'auto',
       'overflow-y': 'hidden',
       'width': '100%',
       'position': 'absolute',
        'top': 0,
       'height': '100%'
    })
     if($('.navFather').parent(). attr("class") != 'grandFather') {
       $('.navFather').parent().addClass('grandFather');
       $('.grandFather').css({
          'position': 'relative',
          'height': "3.7rem",
       })
     }


                    $navChild.eq(0).addClass(active).siblings().removeClass(active);//初始化
                    $navFather.animate({
                        scrollLeft: 0
                    }, 200)
                    var nh = $nav.offset().top, //获取导航栏在文档中的高度
                        sh = $(document).scrollTop(); //获取滚轮高度
                    var start_nH = $nav.offset().top
                    index = $navChild.index() + 1
                    //动态设置nav 宽度  改为  手动添加
                    
//                    $nav.width(function() {
//                        return $navChild.width() * index +'px';
//                    })
                    
                    var Harr = [], //存储item高度容器
                        Warr = []; //存储nav li offsetleft容器
                    $control.each(function(index, el) {
                        Harr.push(el.offsetTop);
                    })
//                    $navChild.each((index, el) => {
//                        Warr.push(el.offsetLeft);
//                    })

                    let lock = false;  //滚动防抖锁
                    $(window).scroll(function() {
                        function _throttle(func, delay = 60) {
                            return(() => {
                                if(lock) {
                                    return
                                };
                                func();
                                lock = true;
                                setTimeout(() => {
                                    lock = false
                                }, delay)
                            })()
                        }
                        _throttle(_scroll, 80); //滚动防抖,防止滚动函数触发频繁,造成滚动卡顿                        
                        function _scroll() {  //滚动触发的函数
                            sh = $(document).scrollTop();
                            if($(window).scrollTop() >= nh) {
                                $navFather.css({
                                    'position': 'fixed',
                                    "top": '0',
                                    'height': $('.navFather').height(),
                                });
                                Harr.forEach((n, i) => {
                                    if($(window).scrollTop() + $nav.height() >= n -  control_top) {  //
                                        //tab切换设置
                                        if(isFollow) {
                                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                                        }
                                    }
                                });

                                //以下是导航跟随逻辑,
                                //思路:计算出scrollLeft =0 时   元素离文档最左侧的距离   -   滚动后元素离  scrollLeft =0 时 元素离文档最左侧的距离  =滚动距离
                                var item = $("." + active);
                                var itemOffset = item.offset();
                                //元素离    scrollLeft等于0  时的距离
                                var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                                //scrollLeft等于0,居中时的offsetLeft
                                var centerLeft = ($navFather.width() - item.width()) / 2;
                                $navFather.stop().animate({
                                    scrollLeft: itemOffsetLeft - centerLeft
                                })
                            } else {
                                $navFather.css({
                                    'position': 'absolute',
                                    "z-index": '7'
                                })
                            }
                        }
                    });
                    if(isFollow == false) {
                        $control.eq(0).css({
                            'display': 'block'
                        }).siblings().css({
                            'display': 'none'
                        });
                        $navChild.click(function() {
                            let i = $(this).index()
                            $control.eq(i).css({
                                'display': 'block'
                            }).siblings().css({
                                'display': 'none'
                            });
                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                            var item = $("." + active);
                            var itemOffset = item.offset();
                            //元素离    scrollLeft等于0  时的距离
                            var itemOffsetLeft = itemOffset.left + $navFather.scrollLeft();
                            //scrollLeft等于0,居中时的offsetLeft
                            var centerLeft = ($navFather.width() - item.width()) / 2;
                            $navFather.stop().animate({
                                scrollLeft: itemOffsetLeft - centerLeft
                            }, 200);
                            $('html,body').animate({
                                scrollTop: $control.eq(i).offset().top - $nav.height()
                            }, 300)
                        })
                    } else {
                        $navChild.click(function() {
                            var i = $(this).index()
                                //点击改变active 改为 滚动监听改变active 
//                            $navChild.eq(i).addClass(active).siblings().removeClass(active);
                            var offsetH = $control.eq(i).offset().top;
                            $('html,body').animate({
                                scrollTop: $control.eq(i).offset().top - $nav.height() - nav_MarginBottom
                            }, 300)
                        })
                    }

                }
            })
    

 

 

3月25日改良  ,

1.功能模块化,逻辑处理简单化,避免代码沉余。

2.减少因动态增加dom带来的性能和布局问题

let obj={
    el:".warp", //根元素
    //初始化
    init(options){
        //初始化数据
        this.handleGetData(options);
        //初始化子集active
        this.$navChild.eq(0).addClass(this.active).siblings().removeClass(this.active);
        //初始化 样式
        this.$navFather.css({
            'overflow-x': 'auto',
            'overflow-y': 'hidden',               
            'position': 'relative',
            'top': 0,                    
        }) 
        //初始化滚动条
        this.$navFather.animate({
            scrollLeft: 0
        }, 200);
        this.nh=this.$nav.offset().top; //获取导航栏在文档中的高度
        //执行函数
        this._scrollFollow();
    },
    //tab切换函数
    handleTba(){  
        
    },
    //跟随滚动函数
    _scrollFollow(){
        let _this=this;
        _this.getHeight() ;
        _this._handleScroll();
        _this.handleClick();        
    },
    //获取滚动条高度和被监听元素的offset数组
    getHeight(){
         let _this=this;     
        //动态设置nav 宽度  改为  手动添加                                     
        _this.Harr = []; //存储item高度容器      
        _this.$control.each(function(index,el) {
            _this.Harr.push(el.offsetTop);
        });  
        console.log(_this.Harr)
     },
     //防抖函数
     _handleThrottle(fnc,delay = 60) {
         const _this=this;
        return(() => {
            if(_this.lock) {
                return
            };
            _this.handleIfScrollTop();
            _this.lock = true;
            setTimeout(() => {
                _this.lock = false
            }, delay)
        })()
    },
    //滚动触发的函数
    _handleScroll(){
        let _this=this;           
        _this.lock = false;  //滚动防抖锁    
         $(window).scroll(function() {  
              _this._handleThrottle(_this.handles,50); //滚动防抖,防止滚动函数触发频繁,造成滚动卡顿                                             
            
        });         
    },
    //判断是否超过导航栏
    handleIfScrollTop(){
        let _this=this;
        if($(window).scrollTop() >= _this.nh) {
            _this.$navFather.css({
                'position': 'fixed',
                "top": '0',
                "z-index": '7',
                'height': _this.$nav.height(),
            });    
            _this.Harr.forEach((n, i) => {
                if($(window).scrollTop() + _this.$nav.height() >= n -  _this.control_top) {  //
                    //active切换设置
                    _this.$navChild.eq(i).addClass(_this.active).siblings().removeClass(_this.active);
                }
           });               
        } else {
            _this.$navFather.css({
                'position': 'relative',
                "z-index": '7'
            })
        }
        _this.handleLeft();  //动态active居中
    },
    //  点击导航条触发函数
    handleClick(){
        let _this=this;
        this.$navChild.click(function() {
            var i = $(this).index()
                //点击改变active 改为 滚动监听改变active 
            var offsetH = _this.$control.eq(i).offset().top;
            $('html,body').animate({
                scrollTop: _this.$control.eq(i).offset().top - _this.$nav.height() - _this.nav_MarginBottom
            }, 300);
        })
    },
    //计算选择active的位置,active动态跟随
    handleLeft(){       
        //以下是导航跟随逻辑,
        //思路:计算出scrollLeft =0 时   元素离文档最左侧的距离   -   滚动后元素离  scrollLeft =0 时 元素离文档最左侧的距离  =滚动距离
        var item = $(".nav_box ." + this.active);
        var itemOffset = item.offset();
        //元素离    scrollLeft等于0  时的距离
        var itemOffsetLeft = itemOffset.left + this.$navFather.scrollLeft();
        //scrollLeft等于0,居中时的offsetLeft距离
        var centerLeft = (this.$nav.width() - item.width()) / 2;
        this.$navFather.stop().animate({
            scrollLeft: itemOffsetLeft - centerLeft
        })
    },
    //处理传入参数
    handleGetData(option) {
        //根据参数的情况,构造出合适的对象,调用 _scrollFollow
        if(arguments.length === 0) {
            //没有参数
            this.obj={};
        } else if(arguments.length === 1) {
            if(arguments[0] instanceof Array) {
                //有一个参数
                this.obj={
                    nav: arguments[0][0],
                    active: arguments[0][1],
                    control: arguments[0][2],
                    isFollow: arguments[0][3],

                }
            } else
            if(arguments[0] instanceof Object) {
                console.log(456)
                this.obj=arguments[0]; //第一个参数就是对象
            }
        } else if(arguments.length === 2) {
            //参数数量大于等于2,这种情况只需要前两个参数即可
            this.obj={
                nav: arguments[0],
                active: arguments[1]
            }
        } else if(arguments.length === 3) {
            //参数数量大于等于3,这种情况只需要前两个参数即可
            this.obj={
                nav: arguments[0],
                active: arguments[1],
                control: arguments[2],
            }
        } else if(arguments.length === 4) {
            //参数数量大于等于4,这种情况只需要前两个参数即可
            this.obj={
                nav: arguments[0],
                active: arguments[1],
                control: arguments[2],
                isFollow: arguments[3],
            }
        }
        //默认参数
        this.objDefault = {
            nav: $('.nav'),
            active: $('.active'),
            control: $('.nav').parent().parent().next().children(),
            isTab:false
        };
            this.data = Object.assign({}, this.objDefault, this.obj);
            this.$nav = $(this.data.nav);          //nav   ul
            this.$navChild =this.$nav.children();  //nav子集     li                
            this.$active = $(this.data.active);     //选中            
            this.active =this.$active[0].className;  //获取选中的class名
            this.$control = $(this.data.control);    //监听的元素  item
            this.isFollow = this.data.isFollow || false; //默认为false
            this.nav_MarginBottom = 10,    //滚动时    nav 距离监听元素的距离  
            this.control_top = this.$control.height() * 0.2;    //nav离  滚动距离到下一个元素 还有多远   
                                                        //当前的$control离nav的距离超过20%,就切换active             
            this.$navFather =this.$nav.parent();  //nav父级
    }
    
}
obj.init(['.nav_box ul', '.nav_box .active', '.con']) //active的父级 , active选中项,被监听滚动的元素

 6月24日  简约版 无横向自动跟随

//初始化样式,避免编辑模式影响
        $('.nav').css({
            'position': 'relative',
            'top': 0,
            'height': $('.nav').parent().height(),
        })
        $('.nav li').eq(0).addClass('active').siblings().removeClass('active')
        var Harr = [];
        $('.commodity_title_box').each(function(i,list){
            Harr.push($(this).offset().top)                        
        })
        $('.nav li').click(function(e){
            e.preventDefault()
            var i = $(this).index();
            $('html,body').stop().animate({
                scrollTop:Harr[i]-$(this).height()-10
            },200)
        })                
        $(window).scroll(function() {
            if($(window).scrollTop() >= $('.nav').parent().offset().top) {
                $('.nav').css({
                    'position': 'fixed',
                    'top': 0,
                    'height': $('.nav').parent().height(),
                    'z-index':'7'
                });    
                Harr.forEach(function(H,i){
                    if(Harr[i]-$('.nav').height()-280<$(window).scrollTop()){
                        $('.nav li').eq(i).addClass('active').siblings().removeClass('active')
                    }
                })                        
            } else {
                $('.nav').css({
                    'position': 'relative',
                    'top': 0,
                    'height': $('.nav').parent().height(),
                })
            }                    
        })

 

持续改良中.....

posted @ 2019-12-16 17:20  混名汪小星  阅读(928)  评论(0)    收藏  举报