@lizlalala
2017-01-05T12:01:17.000000Z
字数 5970
阅读 1362
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 height
if ( 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(){
//1
const scrollTop = document.body.scrollTop||document.documentElement.scrollTop;
//2
const 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 ]
);
//3
const 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.5
lastTime = 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); //!!context
lastTime = now;
}, wait-gap);
}
}
}
css3常用