因为需求,也因为逛了一圈论坛和开源社区,没找到想要的东西,很多都是零散的代码,不能形成一个组件,所以写下这个工具,需要的只管拿去。如有建议欢迎留言。
直入正题。
/**
可嵌套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版本,它在我目前的使用场景下工作的很不错,若有新版本我会持续在博客中更新。