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. Ubuntu 上 执行命令 java -version 显示 没有那个文件或目录

    解决方法 执行 which java 发现默认java目录:/usr/bin/java . 查看 JAVA_HOME 路径:$JAVA_HOME,得到 /usr/local/java/jdk1.7.0 ...

  2. 第五次程序设计作业 C++计算器雏形 调用文件输入输出

    一.C++计算器作业系列链接 第三次作业:C++计算器雏形 第三次作业附加:代码规范 第四次作业:命令行的调用及计算 MyGithub 二.本次作业相关 要求:第五次程序设计作业 根据这一次的作业要求 ...

  3. HDU 4587 TWO NODES(割两个点的最大连通分支数)

    http://acm.hdu.edu.cn/showproblem.php?pid=4587 题意: 给一图,求割去两个点后所能形成的最大连通分支数. 思路: 对于这种情况,第一个只能枚举,然后在删除 ...

  4. shell模拟ctrl c停止

    kill命令可以带信号号码选项,也可以不带. 如果没有信号号码,kill命令就会发出终止信号(15),这个信号可以被进程捕获,使得进程在退出之前可以清理并释放资源. 也可以用kill向进程发送特定的信 ...

  5. MarkChanges: Jmeter

    1. 20180627 调整启动的内存set HEAP=-Xms1024m -Xmx1024m2. 20180627 调整输出格式为xml #jmeter.save.saveservice.outpu ...

  6. 30分钟带你了解Docker

    最近一直在忙项目,不知不觉2个多月没有更新博客了.正好自学了几天docker就干脆总结一下,也顺带增加一篇<30分钟入门系列>.网上能够查到的对于docker的定义我就不再重复了,说说我自 ...

  7. windows批处理命令

    前言 批处理文件(batch file)包含一系列 DOS命令,通常用于自动执行重复性任务.用户只需双击批处理文件便可执行任务,而无需重复输入相同指令.编写批处理文件非常简单,但难点在于确保一切按顺序 ...

  8. C#复制文件

    string pLocalFilePath ="";//要复制的文件路径 string pSaveFilePath ="";//指定存储的路径 if (File ...

  9. Linux crontab定时执行任务 命令格式与详细例子(转)

    基本格式 : * * * * * command 分 时 日 月 周 命令 第1列表示分钟1-59 每分钟用*或者 */1表示 第2列表示小时1-23(0表示0点) 第3列表示日期1-31 第4列表示 ...

  10. 3.4 复杂的x86指令举例

    计算机组成 3 指令系统体系结构 3.4 复杂的x86指令举例 x86作为复杂指令系统的代表,自然会有不少相当复杂的指令.在这一节我们将会看到其中有代表性的一些例子. 关于复杂的x86指令,我们这里举 ...