@lizlalala
2017-01-05T04:01:17.000000Z
字数 5970
阅读 1572
css js underscore 无限滚动 throtte
小记:
有几个重要点呐:
- input 的id与 label的for属性对应,使得label可以检测input的check事件,否则点label是不能触发check的。
- css3中:checked 属性," + "兄弟选择符。使得可以对选中状态下的label进行css编写
- todo 用transform,尽量不用left这些。以达到平滑效果 【done】
确实过渡会平滑很多。
recipe:
这边是以一个小的div为例。
如果想全局滚动条,类似于阮一峰es6教程上的那种效果。
则应该相应的变为
$(window).scrollTop()$(window).height()$(document).height()
补充知识:
element.scrollTop是滚动过程中文档超出视窗的部分,它的最大值就是document.height-element.height,即为progressbar为100%的时候。
两幅图可以说明:

还有一种纯css做的方法
CSS only scroll indicator。思路主要就是用一个斜对角的图形,然后z-index =-1,只留出height为比如2px的区域,这样滚动条动时,显示的部分就在慢慢变宽。
补充
if ( jQuery.isWindow( elem ) ) {return elem.document.documentElement[ "client" + name ];}// Get document width or heightif ( elem.nodeType === 9 ) {doc = elem.documentElement;// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.return Math.max(elem.body[ "scroll" + name ], doc[ "scroll" + name ],elem.body[ "offset" + name ], doc[ "offset" + name ],doc[ "client" + name ]);}
很久以前我萌还在用jquery的时候,很喜欢用的一个函数是trigger,但是离开了jquery,还有什么方法可以实现同样的问题,楼主一直很困惑...一直到看了mdn的相关api,具体demo请戳自定义事件及触发
划重点就是:
trigger event@luchenCodePen
var event = new Event()target.addEventListener("自定义名称")target.dispatchEvent(event);
references:
1. 创建和触发 events
2. mouseevent
todo
楼主在开发这部分组件,用的是原生的html5的input中的type=range,实现了这么几个功能:
html5 input(type:range)
选择器:
楼主最近在用vue开发cnode的移动端,首页部分有滚动列表,因为我们之前提到过debounce延缓input框发送ajax联想请求。那么这边我们就需要用throtte去实现无限滚动。(无限滚动的原理就是在滚动事件+分页请求+节流)
具体为什么不用debounce,需要体会的是debounce是在停止项某事件或者超过某个限时时才执行一次。滚动并不需要用户在停止scroll才去加载,而是应该在滚动的过程中定时去请求。所以用throtte。
throtte的实现是最简单的版本:
function throtte(func,wait,options){let lastTime = Date.now();return function next(){const now = Date.now();const gap = now - lastTime;if( gap >= wait ){func.apply(this,arguments);// if(param===options.terminal) return;lastTime = now;}}}//调用created(){this.fetchPage(1);this.infiniteScroll = this.infiniteScroll.bind(this);window.addEventListener("scroll",this.infiniteScroll());},infiniteScroll(){return throtte(this.fetchWhenScroll.bind(this),1000);},
在实现过程中,还有这么几个问题:
楼主最开始的思路:方向检测+缓存。直接在滚动时发送请求(给定page初始值,根据上下滑动方向page++/page--),同时把每一页的数据存储下来。下次发送请求前,先根据页数判断是否已有缓存。上滑的话,不请求,满足需求。上滑一段时间后下滑,因为该页数已经访问过,数据已有,那么也不请求。满足需求。
所以,现在的关键问题在于:怎么检测这个scroll的上下顺序
查阅了资料,发现普遍是通过前后两次的scrollTop来检测的。楼主写了一个
function isScrollDown(initTop,cb){const curTop = document.documentElement.scrollTop;let isDown = false;if(curTop>initTop){isDown = true;cb(curTop);return isDown;}
//调用,滚动检测fetchWhenScroll(){const isDown = isScrollDown(this.initTop,(curTop)=>{this.initTop = curTop;);isDown? this.page++ : this.page--; //1,方向检测this.fetchPage(this.page);},fetchPage(page){if(responseDict[page]) return; //2,缓存判断axios.get('https://cnodejs.org/api/v1/topics',{params:{page,...}}).then(response=>{if(response.data){const appendedList = this.getTransformedResponse(response.data);responseDict[page] = appendedList; //3,缓存数据this.postList = [...this.postList,...appendedList];}}).catch(err=> console.log(err));}
scrollTop是文档的top=0处位置,到window的top=0位置的间距。具体的可以看上面scrollbar的示意图。
但是仔细一想,实际上是有问题的。当下滑时,会不停的page++,fetch serverlist。当文档实际上已经结束时。由于还在滚动,page会继续++。,所以要加一个if(response.data)的判断。如果没有数据,就啥也不干。
第二种思路,是看一篇无限滚动的文章看到的(Debouncing and Throttling Explained Through Examples)。主要原理是检测离底部的距离。当快接近底部时发送请求。
视窗底部距离文档底部的距离:
let height = $(document).height() - $(window).scrollTop() -$(window).height();
应用场景:
function check_if_needs_more_content(){//1const scrollTop = document.body.scrollTop||document.documentElement.scrollTop;//2const doc = document.documentElement;const name = 'Height';const docHeight = Math.max(document.body[ "scroll" + name ], doc[ "scroll" + name ],document.body[ "offset" + name ], doc[ "offset" + name ],doc[ "client" + name ]);//3const windowHeight = window.innerHeight || document.documentElement.clientHeight;let distanceToDocBottom = docHeight-windowHeight-scrollTop;if(Math.abs(distanceToDocBottom)<300) return true;return false;}//调用fetchWhenScroll(){if(check_if_needs_more_content()){this.fetchPage(this.page);this.page++;}},
这种方法的好处在于,避免方向的判断,上滑的时候 distanceToDocBottom实际上是变大的,那么就不需要进行请求。同时由于距离底部的距离限定好,那么从本次加载数据,到下次加载数据,实际上是把本次获得的数据滚动完以后再进行加载的。但是有一点问题是,貌似滑动到最下面的时候会停止,需要上滑一部分才会响应。而且当滚动很多页的时候,distanceToBottom会大于设定的gap。就不会发送请求。待解决。
除此之外,因为公式是jquery版本的。楼主试了很多兼容的原生版本,但是都不太对...就暗搓搓的去github上面搜了jquery这部分的源码...捂脸。
===2016/11/23 更新
楼主的throtte是有点问题的,看了下underscore的函数说明,有两点:
function throtte(func,wait,options){let isFirst = true;let funcId,gap,lastTime,duration = 0;const finalOptions = Object.assign({},{leading: true,trailing: true},options);if(!finalOptions.leading && !finalOptions.trailing) {alert("不可以同时设置leading,trailing为负");return;}return function next(){const now = Date.now();const context = this;if( isFirst || (gap = now -lastTime) >= wait ){//=====更新 2017.1.5lastTime = now;if(isFirst){isFirst = false;//leading:false时,不执行,等下一次wait的时间以后if(finalOptions.leading)func.apply(context,arguments);}}else{// lastTime-lastTime+wait间进入的函数,需要取消。结束再执行clearTimeout(funcId);//trailing true时才执行。if(finalOptions.trailing)funcId = setTimeout(function(){func.apply(context,arguments); //!!contextlastTime = now;}, wait-gap);}}}
css3常用