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. Linux 操作 mysql

    linux mysql 操作命令 [转 来源] 1.linux下启动mysql的命令:mysqladmin start/ect/init.d/mysql start (前面为mysql的安装路径) 2 ...

  2. Linux 安装 mysql 转 http://www.cnblogs.com/fnlingnzb-learner/p/5830622.html

    到mysql官网下载mysql编译好的二进制安装包,在下载页面Select Platform:选项选择linux-generic,然后把页面拉到底部,64位系统下载Linux - Generic (g ...

  3. 02_Spark Application不同模式下的监控

    监控Spark Application的运行 官方文档: http://spark.apache.org/docs/latest/monitoring.html 1.1 监控方式 Driver Pro ...

  4. UVa 1220 Hali-Bula的晚会(树的最大独立集)

    https://vjudge.net/problem/UVA-1220 题意: 公司里有n个人形成一个树状结构,即除了老板以外每个员工都有唯一的直属上司.要求选尽量多的人,但不能同时选择一个人和他的直 ...

  5. NOI 16 买房子

    买房子(NOI 16) 总时间限制: 1000ms 内存限制: 65536kB 描述 某程序员开始工作,年薪N万,他希望在中关村公馆买一套60平米的房子,现在价格是200万,假设房子价格以每年百分之K ...

  6. HDU 1043 Eight(双向BFS+康托展开)

    http://acm.hdu.edu.cn/showproblem.php?pid=1043 题意:给出一个八数码,求出到达指定状态的路径. 思路:路径寻找问题.在这道题里用到的知识点挺多的.第一次用 ...

  7. testNG 学习笔记 Day 3 常用的断言

    TestNG中最常用的一个断言类是Assert.java,里面有多个静态方法,这个类我们习惯叫硬断言.对应的还有一个软断言的类,叫SoftAssert.java,这个类是需要创建实例对象,才能调用相关 ...

  8. CentOS 6.5安装配置LAMP服务器(Apache+PHP5+MySQL)的方法

    CentOS 6.5安装配置LAMP服务器(Apache+PHP5+MySQL)的方法 准备篇: 1.配置防火墙,开启80端口.3306端口vi /etc/sysconfig/iptables-A I ...

  9. spring boot 开发 提交form表单出错

    提交表单时,字段有的没有值,springboot 会报错. org.springframework.validation.BindException: org.springframework.vali ...

  10. 《剑指offer》第九题(用两个栈实现队列)

    // 面试题:用两个栈实现队列 // 题目:用两个栈实现一个队列.队列的声明如下,请实现它的两个函数appendTail // 和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的 ...