scrollTop的兼容性

scroll事件,当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加载页面的滚动条。

虽然scroll事件是在window对象上发生,但他实际表示的则是页面中相应元素的变化。在混杂模式(document.compatMode的值为BackCompat)下,可以通过<body>元素的scrollLeft和scrollTop来监控到这一变化。

而在标准模式(document.compatMode的值为CSS1Compat)下,除Safari之外的所有浏览器都会通过<html>元素来反应这一变化。

以上内容来自《Javascript 高级程序设计(第三版)》。

以下是我自己测试的结果,截止2017-05-18,用的是最新版的chrome、Firefox和Win7中的IE。

  • 混杂模式下,chrome、IE、Firefox都是通过document.body.scrollTop监听滚动条的位置。
  • 标准模式下,chrome通过document.body.scrollTop监听滚动条位置,IE和Firefox通过document.documentElement.scrollTop监听滚动条位置

可以用下面的代码进行验证:

    function outPutScrollTop() {
console.log(document.compatMode);
if(document.compatMode === 'CSS1Compat') {
console.log(document.documentElement.scrollTop + '标准模式');
console.log(document.body.scrollTop + '标准模式');
} else {
console.log(document.body.scrollTop + '混杂模式');
}
}
// 绑定监听
window.addEventListener('scroll', outPutScrollTop);

(去掉文档头部的文档声明就可以开启混杂模式。)

scroll事件的优化

scroll事件如果不做优化,默认情况下会频繁地被触发,如果在事件处理程序内进行了复杂的DOM操作,就会大大增加浏览器的负担,消耗性能。

通过看别人的文章,知道了可以通过防抖函数和节流函数对这种频繁触发的事件进行优化。

防抖函数达成的效果是:scroll事件被频繁触发时,不会每次都执行事件处理程序中的关键代码,当滚动条停止滚动时,经过事先设置好的时间间隔后才会执行真正想要执行的代码。

节流函数不像防抖函数那样只在用户停止滚动时才执行事件处理程序中的关键代码,而是在用户滚动滚动条的过程中每隔一定的时间必执行一次事件处理程序中的关键代码。

以下是对别人文章的总结。

防抖函数

简单的防抖优化:

    function test() {
console.log('func');
} window.addEventListener('scroll',function(event) {
clearTimeout(test.timer);
test.timer = setTimeout(test,500);
},false);

将上面的代码封装成一个函数:

function debounce(fun,t,immediate) {
var timeout;
//返回真正的scroll事件的事件处理程序
return function(event) {
var that = this, arg = arguments;
var later = function() {
timeout = null;
if(!immediate) fun.apply(that,arguments);
};
var callNow = immediate && !timeout;//这一句位置很重要
clearTimeout(timeout);
timeout = setTimeout(later,t);
if(callNow) {
fun.apply(that,arguments);
}
}
};

debounce函数接收三个参数:

第一个参数是一个函数,该函数是事件处理程序中真正想要执行的代码。

第二个参数是数字,单位毫秒,表示间隔多久调用一次作为第一个参数的函数。这个参数不能小于当前浏览器的最小时间间隔(不同的浏览器的最小时间间隔不同,一般在10~20毫秒,HTML5规范中规定是4毫秒),如果这个参数等于或小于这个最小时间间隔,那么和没有优化没有区别。事实上,未优化时,scroll事件频繁触发的时间间隔也是这个最小时间间隔。

第三个参数是一个布尔值,不传或为false时,最终的效果与开始那个简单的防抖优化的效果一样;当为true时,表示滚动开始时执行一次作为第一个参数的函数,滚动停止时不执行。

用法:

var myEfficientFn = debounce(function() {
// 滚动中的真正想要执行的代码
console.log('ok' + new Date());
}, 500, false); // 绑定监听
window.addEventListener('scroll', myEfficientFn);

下面是underscore.js里封装的防抖函数:

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result; var later = function() {
var last = _.now() - timestamp; if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
}; return function() {
context = this;
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
} return result;
};
};

节流函数

简单的节流函数:

function throttle(fun,t,mustRun,denyLast) {
var timer = null;
var startTime = 0;
return function(event) {
var that = this, args = arguments;
clearTimeout(timer);
var later = function() {
timer = null;
if(denyLast) fun.apply(that,args);
console.log('执行的是later.');
};
var currTime = new Date().getTime();
if(currTime - startTime >= mustRun) {
console.log(currTime - startTime);
fun.apply(that,args);
startTime = currTime;
} else {
timer = setTimeout(later,t);
}
};
}

这个节流函数的整体结构与防抖函数的类似,相比防抖函数,节流函数内部多了一个对时间间隔的判断。

上面这个节流函数接收四个参数:

第一个参数是一个函数,表示当scroll事件被触发时,开发者真正想要执行的关键代码。

第二个参数是一个数字,单位毫秒,实际上是要传入setTimeout()方法的第二个参数。(这里setTimeout()的作用就是防止事件处理程序中的关键代码频繁地执行)

第三个参数也是一个数字,单位毫秒,表示在该时间段内必执行一次关键代码。

第四个参数是一个布尔值,表示在滚动停止时,是否要执行一次关键代码。true表示执行,false表示不执行。

在上面的节流函数中,因为startTime是在外部函数中初始化的,所以滚动开始时必会执行一次关键代码。

节流函数的用法示例:

var myEfficientFn = throttle(function() {
// 滚动中的真正想要执行的代码
console.log('ok' + new Date());
}, 500,1000,false); // 绑定监听
window.addEventListener('scroll', myEfficientFn);
//或者这样,效果是一样的
window.addEventListener('scroll',throttle(function() {
// 滚动中的真正想要执行的代码
console.log('ok' + new Date());
}, 500,1000,false));

underscore.js里封装的节流函数:

// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};

上面的防抖函数和节流函数可以应用到所有类似scroll事件这种频繁被触发的事件的优化,比如resize事件、键盘事件、鼠标滚轮事件等。

(完)

参考文章:

1.【前端性能】高性能滚动 scroll 及页面渲染优化

2.函数防抖与节流

3.setTimeout 和 setInterval最小执行时间问题

4.关于setTimeout()你所不知道的地方

scroll事件的优化以及scrollTop的兼容性的更多相关文章

  1. 前端资讯周报 3.13 - 3.19: WebVR来了!以及如何优化scroll事件性能

    每周一我都会分享上一周我订阅的技术站点中,和解决问题的过程中阅读到的值得分享的文章.这是迫使我学习的一个动力 本周推荐 Minecraft in WebVR with HTML Using A-Fra ...

  2. 如何提高scroll事件的性能

    1. chrome devtool 是诊断页面滚动性能的有效工具 2. 提升滚动时性能,就是要达到fps高且稳. 3. 具体可以从以下方面着手 使用web worker分离无页面渲染无关的逻辑计算 触 ...

  3. 最优-scroll事件的监听实现

    1. 背景和目标 前端在监听scroll这类高频率触发事件时,常常需要一个监听函数来实现监听和回调处理.传统写法上利用setInterval或setTimeout来实现. 为了减小 CPU 开支,往往 ...

  4. “如何稀释scroll事件”的思考(不小心写了个异步do...while)

    看了下园友的一帖子:http://www.cnblogs.com/xzhang/p/4145697.html#commentform 本来以为是很简单的问题,但仔细想想还挺有意思的.简单的说就是增加事 ...

  5. jQuery scroll事件实现监控滚动条分页示例(转)

    这篇文章主要介绍了jQuery scroll事件实现监控滚动条分页简单示例,使用ajax加载,同时介绍了(document).height()与$(window).height()的区别,需要的朋友可 ...

  6. scroll事件实现监控滚动条并分页显示示例(zepto.js)

    scroll事件实现监控滚动条并分页显示示例(zepto.js  ) 需求:在APP落地页上的底部位置显示此前其他用户的购买记录,要求此div盒子只显示3条半,但一页有10条,div内的滑动条滑到一页 ...

  7. JQUERY 滚动 scroll事件老忘记 标记下

    制作笔记 这个scroll事件 老忘记.... 写的太垃圾了  希望有路过的大神指点的吧~ 这个貌似应该写个函数里 调用好些的吧~  写个类这样的 也方便扩展貌似  不过就是想想  ~ $(windo ...

  8. jQuery scroll事件

    scroll事件适用于window对象,但也可滚动iframe框架与CSS overflow属性设置为scroll的元素. $(document).ready(function () { //本人习惯 ...

  9. JavaScript中作用域回顾(避免使用全局变量)(瀑布流的实现)(scroll事件)以及Django自定义模板函数回顾

    页面显示照片样式为瀑布流: 上面的div个数可以按照自己安排进行划分.img的分布可以使用模板标签以及自定义模板函数进行排布: 自定义模板函数实现可以看,最后几列:python---django中模板 ...

随机推荐

  1. python 递增递减数列

    def is_arithmetic(l): delta = l[] - l[] ): ] - l[index] == delta): return False return True print(is ...

  2. Qt5_qtconfig

    1.http://tieba.baidu.com/p/3225596765 QtConfig was removed in Qt5. If you want to force Qt5 to use a ...

  3. 消息/事件, 同步/异步/协程, 并发/并行 协程与状态机 ——从python asyncio引发的集中学习

    我比较笨,只看用await asyncio.sleep(x)实现的例子,看再多,也还是不会. 已经在unity3d里用过coroutine了,也知道是“你执行一下,主动让出权限:我执行一下,主动让出权 ...

  4. 【Golang】Debug :decoding dwarf section info at offset 0x0: too short

    解决方法 通过下面的方式升级dlv 来解决这个问题: go get -u github.com/derekparker/delve/cmd/dlv 下面是我记录的定位问题的过程 问题描述 博主升级到了 ...

  5. RabbitMQ入门_08_所谓的点对点与发布订阅模型

    A. JMS 模型 JMS 中定义了点对点和发布订阅两种消息模型,原来以为 AMQP 协议中 direct Exchange 对应点对点模型,topic Exchange 对应发布订阅模型,fanou ...

  6. threejs和3d各种效果的学习

    写给即将开始threejs学习的自己,各种尝试,各种记忆.不要怕,灰色的年华终会过去. 一个技术学习的快慢,以及你的深刻程度,还有你的以后遇到这个东西的时候的反应速度,很大程度上,取决于你的博客的深刻 ...

  7. 雷林鹏分享:C# 命名空间(Namespace)

    C# 命名空间(Namespace) 命名空间的设计目的是为了提供一种让一组名称与其他名称分隔开的方式.在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突. 定义命名空间 命名 ...

  8. Android之侧滑菜单DrawerLayout的使用

    在android support.v4 中有一个抽屉视图控件DrawerLayout.使用这个控件,可以生成通过在屏幕上水平滑动打开或者关闭菜单,能给用户一个不错的体验效果. DrawerLayout ...

  9. Confluence 6 启用嵌套用户组

    一些目录服务器能够允许你在一个组中定义另外一个组.在这种结构下的用户组称为用户组嵌套.嵌套组的配置能够让子用户组继承上级用户组的权限,使系统的权限配置变得简单. 这个页面描述了 Confluence ...

  10. Web Service基本概念

    Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的独立的通讯技术.是:通过SOAP ...