web列表容器1.0 (可以为所欲为固定列表顶部的工具类)

因为需求,也因为逛了一圈论坛和开源社区,没找到想要的东西,很多都是零散的代码,不能形成一个组件,所以写下这个工具,需要的只管拿去。如有建议欢迎留言。

直入正题。

/**
    可嵌套web列表容器

    author 万相明 2017.12
*/
var NestList={
    createNew:function(Flag){
        var NestList=$("<div state='3'></div>");

        var cb=CB.createNew("<div>"+
                "   <div ref=\'headContainer\' style='overflow:hidden'><div ref=\'head\'></div></div>"+
                "   <div ref=\'listContainer\' style='overflow:hidden'><ul ref=\'list\'></ul></div>"+
                "   <div ref=\'footContainer\' style='overflow:hidden'><div ref=\'foot\'></div></div>"+
                "</div>");
        cb.appendTo(NestList);

        NestList.CB=cb;

        var head=cb.get("head");
        var listUl=cb.get("list");
        var foot=cb.get("foot");

        var listAry=[];
        var itemAry=[];

        //该方法返回li标签,为了让客户端程序员能够全方位定制样式
        NestList.putNestList=function(PNestList){
            var li=$("<li></li>");
            li.appendTo(listUl);
            PNestList.appendTo(li);
            listAry.push(PNestList);
            return li;
        }

        //该方法返回li标签,为了让客户端程序员能够全方位定制样式
        NestList.putItem=function(Item){
            var li=$("<li></li>");
            li.appendTo(listUl);
            Item.appendTo(li);
            itemAry.push(Item);
            return li;
        }

        //每个list都应该有一个独有的title
        NestList.getFlag=function(){
            return Flag;
        }

        NestList.isThis=function(TheFlag){
            return Flag==TheFlag;
        }

        NestList.findNestList=function(TheFlag){
            var list=null;
            $.each(listAry,function(index,val){
                if(val.isThis(TheFlag)){
                    list=val;
                }
                else{
                    var li=val.findNestList(TheFlag);
                    if(li != null){
                        list=li;
                    }
                }
            });
            return list;
        }

        NestList.getItemList=function(){
            return itemAry;
        }

        NestList.getHeadElement=function(){
            return head;
        }

        NestList.getContainerElement=function(){
            return listUl;
        }

        NestList.getFootElement=function(){
            return foot;
        }

        //将头部钉在顶部。list向上滑动时,head会留在屏幕的顶端,不会随滑动滑出屏幕
        NestList.pinHead=function(pinStance=0){
            $(window).scroll(function(){
                if(instansToWindowTop(cb.get("listContainer"))-cb.get("headContainer").height() <= pinStance){
                    state1(NestList,pinStance);
                }
                else{
                    state3(NestList);
                }
            });
        }

        //固定底部
        NestList.pinFoot=function(pinStance=0){
            $(window).scroll(function(){
                rol();
            });
            rol();
            function rol(){
                var h=$(window).height();
                var h1=instansToWindowBottom(cb.get("listContainer"))-NestList.CB.get("listContainer").height();
                if(h1 < pinStance){
                    //不固定
                    NestList.attr("state","3")
                    NestList.CB.get("listContainer").css("margin-bottom","0px");
                    NestList.CB.get("footContainer").css("position","static");
                    NestList.CB.get("footContainer").css("top",null);
                }
                else{
                    //固定
                    NestList.attr("state","1");
                    NestList.CB.get("listContainer").css("margin-bottom",NestList.CB.get("footContainer").outerHeight()+"px");
                    NestList.CB.get("footContainer").css("position","fixed");
                    NestList.CB.get("footContainer").css("width","100%");
                    NestList.CB.get("footContainer").css("top",h-pinStance);
                }
            }
        }

        NestList.pinSubHead=function(pinStance=0){
            $(window).scroll(function(){
                $.each(listAry,function(ind,val){
                    if(ind == 0){
                        if(val.attr("state") != 2){
                            //h是第一个元素的头距离固定点的距离
                            var h=instansToWindowTop(val.CB.get("listContainer"))-pinStance-val.CB.get("headContainer").height();
                            var k=false;
                            $.each(listAry,function(i,v){
                                if(i != 0 && v.attr("state") != 3){
                                    k=true;
                                }
                            });
                            if(h <= 0){
                                if(k){
                                    state3(val);
                                }
                                else{
                                    state1(val,pinStance);
                                }
                            }
                            else{
                                state3(val);
                            }
                        }
                    }
                    else{
                        var A=listAry[ind-1];
                        var B=val;
                        var C=listAry[ind+1];
                        //A元素头部距离屏幕顶部距离
                        var h3=instansToWindowTop(A.CB.get("headContainer"));
                        //B元素头部距离屏幕顶部距离
                        var h4=instansToWindowTop(B.CB.get("headContainer"));
                        //A元素头部高度
                        var h5=A.CB.get("headContainer").height();
                        //B元素的内容距离屏幕顶部的距离-固定点高度-B元素头部高度,h6如果大于0,说明B元素的头要归位
                        //这里要使用B元素的内容来测距,因为此时B元素的头部被固定在固定点,无法用于测距判断
                        var h6=instansToWindowTop(B.CB.get("listContainer"))-pinStance-B.CB.get("headContainer").height();
                        //向下滑动时,A元素距离固定点的距离,h7如果大于0,说明A元素的头要浮起
                        var h7=instansToWindowTop(A.CB.get("headContainer"))-pinStance;
                        if(h4 <= h3+h5 && A.attr("state") == 1 && B.attr("state") == 3){
                            //B元素的顶部高于A元素的底部,且A处在浮起状态,B处在初始状态时
                            state2(A,B);
                        }
                        if(h4 <= pinStance && A.attr("state") == 2 && B.attr("state") == 2){
                            //AB处于联动状态,B已经到达固定点
                            state3(A);
                            if(C != null && C.attr("state") == 1){
                                state3(B);
                            }
                            else{
                                state1(B,pinStance);
                            }
                        }
                        if(h6 > 0 && A.attr("state") == 3 && B.attr("state") == 1){
                            //B元素的内容已经下降到低于头的高度与固定点的和
                            //此时B元素的头要归位,AB要联动
                            state3(B);
                            state2(A,B);
                        }
                        if(h7 > 0 && A.attr("state") == 2 && B.attr("state") == 2){
                            //A元素的内容已经下降到低于头的高度与固定点的和,且AB处于联动状态
                            //此时A元素要浮起,B元素要归位
                            state1(A,pinStance);
                            state3(B);
                        }
                    }
                });
            });
        }

        //各种状态的切换要遵循先“填充”后“挖空”的策略,否则在配合某些流加载控件时
        //在特殊状态下会发生因为先挖空导致流加载触发所引发的BUG
        //下述三种状态只适用于head
        //状态1 浮起
        function state1(ele,pinStance){
            ele.attr("state","1");
            ele.CB.get("listContainer").css("margin-top",ele.CB.get("headContainer").height()+"px");
            ele.CB.get("headContainer").css("position","fixed");
            ele.CB.get("headContainer").css("width","100%");
            ele.CB.get("headContainer").css("top",pinStance);
        }
        //状态2 联动
        function state2(A,B){
            A.attr("state","2");
            B.attr("state","2");

            //固定B元素
            B.CB.get("headContainer").css("position","absolute");
            B.CB.get("headContainer").css("top",instansToWindowTop(B.CB.get("headContainer")));

            //将A元素添加到B元素的前面
            B.CB.get("headContainer").before(A.CB.get("headContainer"));

            //恢复AB元素的状态
            B.CB.get("headContainer").css("position","static");
            A.CB.get("headContainer").css("position","static");
            A.CB.get("listContainer").css("margin-top","0px");
            A.CB.get("headContainer").css("top",null);
        }
        //状态3 归位(初始状态)
        function state3(ele){
            ele.attr("state","3")
            ele.CB.get("listContainer").before(ele.CB.get("headContainer"));
            ele.CB.get("listContainer").css("margin-top","0px");
            ele.CB.get("headContainer").css("position","static");
            ele.CB.get("headContainer").css("top",null);
        }

        //元素的顶部距离屏幕顶部距离
        function instansToWindowTop(element){
            return element.offset().top - (document.documentElement.scrollTop || document.body.scrollTop);
        }

        //元素的顶部距离屏幕底部的距离
        function instansToWindowBottom(element){
            return $(window).height()-(element.offset().top - (document.documentElement.scrollTop || document.body.scrollTop));
        }

        return NestList;
    }
}

其中CB类见我的第一篇博客。

简单介绍一下这个类。它是一个专门用来实现web列表的类,接口如下:
putNestList(NestList ns) //向列表插入另一个NestList
putItem(jQueryDom item) //向列表插入dom元素
getFlag() //获取该列表的标识符
isThis(String flagName) //通过列表标识符判断是否是目标列表
findNestList(String flagName) //通过列表标识符查询在此NestList中的子NestList
getItemList() //获取被插入的dom元素数组
getHeadElement() //获取头部
getContainerElement() //获取容器
getFootElement() //获取脚部
pinHead(int distance) //将头部固定在指定高度
pinFoot(int distance) //将脚部固定在指定高度
pinSubHead(int distance) //将子NestList的头部固定在指定高度(只针对头部)

这些接口是我在使用中需求的,如果你想使用它,在使用的过程中需要其他接口,自行修改代码吧。

NestList类如其名,是一个可嵌套列表,你可以将多个NestList通过putNestList的方式嵌套起来,用以实现某些多层次的列表需求。

NestList实现了三个固定元素的pin….方法,分别是当屏幕向上滑动时,将头部固定在距离屏幕顶部指定距离的pinHead;当屏幕向下滑动时,将脚部固定在距离屏幕底部指定距离的pinFoot;以及将所有直接子NestList的头部固定在距离屏幕顶部指定距离的pinSubHead方法。

其中pinSubHead方法,实现了在屏幕向上滑动时,各个子NestList的头部,平滑替换相继固定在顶部的效果。至于相继固定在底部的效果,可以参照pinSubHead来做,因为我还没有遇到过这样的需求,所以没有花时间去扩展此功能。

举个栗子:

<html>
<body>
    <script>
        //屏幕太长看不出效果那就多putItem几条数据,这里做演示就不多加了。
        var lp=NestList.createNew("food");
        lp.appendTo(document.body);
        lp.pinSubHead();

        var sl1=NestList.createNew("fruit");
        $("<div>水果</div>").appendTo(sl1.getHeadElement());
        sl1.putItem($("<div>苹果</div>"));
        sl1.putItem($("<div>葡萄</div>"));
        sl1.putItem($("<div>梨</div>"));

        var sl2=NestList.createNew("drink");
        $("<div>饮料</div>").appendTo(sl2.getHeadElement());
        sl2.putItem($("<div>可乐</div>"));
        sl2.putItem($("<div>雪碧</div>"));
        sl2.putItem($("<div>水</div>"));

        lp.putNestList(sl1);
        lp.putNestList(sl2);
    </script>       
</body>
</html>

效果如下:
效果图

代码被越多的使用场景锤炼,就越健壮,越可靠。NestList还在持续完善中,如同标题,这是1.0版本,它在我目前的使用场景下工作的很不错,若有新版本我会持续在博客中更新。

猜你喜欢

转载自blog.csdn.net/sdfgedcx/article/details/78764345