28

DOM基础小测27期答疑文字版-窗体滚动二三事

 5 years ago
source link: https://www.zhangxinxu.com/wordpress/2019/01/dom-quiz-27-window-scroll/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=8425

本文可全文转载,个人网站无需授权,只要保留原作者、出处以及文中链接即可,任何网站均可摘要聚合,商用请联系授权。

Bni63mQ.jpg!web

一、题目与考察点

题目如下(纸质打印拍摄图):

RJNB7fM.jpg!web

是相当简单的一道题目,入门级别的,虽然挺简单,但还有很多细节是很多人不知道的,因此还是很有价值的。

本题主要考察窗体滚动,窗体高度获取,普通元素高度获取这几个知识点。

本次B站直播在上午月10:15分开始,持续约40分钟,有录播,可以直接下面浏览。

二、答疑内容

大家回答地址这里: https://github.com/zhangxinxu/quiz/issues/4

1. 关于节流函数

有不少人答题时候使用了节流函数,如下:

function Throttle(fn, delay) {
  let last = 0,
  timer = null;
  return function() {
    let context = this,
    args = arguments,
    now = +new Date();
    if (now - last < delay) {
      clearTimeout(timer)
      timer = setTimeout(function() {
         last = now;
         fn.apply(context, args);
      }, delay)
    } else {
      last = now;
     fn.apply(context, args);
    }
  }
}

代码高亮版本见下图:

feAFFzB.png!web

初衷是好的,滚动是一个高频触发的操作,通过节流函数可以降低计算方法执行的频率。但是,实际上,我们开发的大多数页面是如此的简单,根本用不到需要节流函数,反而增加了代码的复杂度,对于本题,可以不需要。

2. 窗体滚动事件绑在哪里?

浏览器窗体滚动事件绑在哪个对象上呢?是 window 对象,还是 document 对象,或者是 document.documentElementdocument.body

我们不妨测试下:

window.addEventListener('scroll', function () {
    console.log('window滚动触发,window.pageYOffset是:' + this.pageYOffset);
});
document.addEventListener('scroll', function () {
    console.log('document滚动触发,document.scrollTop是:' + this.scrollTop);
});
document.documentElement.addEventListener('scroll', function () {
    console.log('document.documentElement滚动触发,document.documentElement.scrollTop是:' + this.scrollTop);
});
document.body.addEventListener('scroll', function () {
    console.log('document.body滚动触发,document.body.scrollTop是:' + this.scrollTop);
});

您可以识别此二维码:

JrqYnev.png!web

或者直接点击这个页面: 可以触发scroll事情的滚动容器测试demo

结果无论是PC,还是移动端,测试结果如下:

B7vQnuv.png!web

也就是 window 对象和 document 对象绑定scroll事件可以触发, document.documentElementdocument.body 是不行的。

然后,直播的时候有人在群里反馈,自己的手机 document 滚动无法触发,如果这是真的,那安全起见,默认的浏览器窗体滚动事件还是绑定在 window 对象上。

3. 窗体的滚动高度获取

如何获取窗体的滚动高度呢?常见的有下面3种代码:

window.pageYOffset;
document.documentElement.scrollTop;
document.body.scrollTop;

都是有效的吗?

我们不妨测试下,想办法识别此二维码:

AVfUVvv.png!web

或者直接访问这个页面: 3种窗体滚动高度获取方法测试demo

结果在PC上是这样:

Vj2aEjZ.png!web

而在手机上则是:

yEJj2q2.png!web

可以看到桌面浏览器和移动端浏览器对于滚动高度获取是有差别的,桌面端浏览器不能使用 document.body.scrollTop 获取浏览器窗体的滚动高度,而移动端不能使用 document.documentElement.scrollTop 获取浏览器窗体的滚动高度。但是都支持 window.pageYOffset

所以,理论上讲,浏览器窗体的滚动高度获取使用 window.pageYOffset 即可,然而 window.pageYOffset 有一个缺点,就是IE9及其以上浏览器才支持,在PC端,很多项目是需要兼容IE8浏览器的,因此,对于传统PC网站,获取浏览器窗体滚动高度比较好的表达方法是这样:

var winScrollTop = window.pageYOffset || document.documentElement.scrollTop;

3. 浏览器窗体高度获取

这个可以使用 window.innerHeight 获取。然而, window.innerHeight 有兼容性问题,IE8浏览器及其以下浏览器是不支持的,怎么办?可以借助 document.documentElement.clientHeight 获取。

于是:

pre>var winHeight = window.innerHeight || document.documentElement.clientHeight;

document.documentElement 是个很特殊的对象,他的很多行为表现跟普通元素是不一样的。例如,普通div这类元素的 clientHeight 是不包括边框大小的,但是, document.documentElement.clientHeight 直接无视这些,无论你 <html> 元素是否设置了 border ,都是页面可视区域的高度。更神奇的是 document.documentElement.offsetHeight 居然是包含浏览器滚动高度的完整高度,等同于 document.documentElement.scrollHeight ,大家可以特殊记忆下。

4. 普通元素的滚动和高度获取

普通元素的滚动直接添加scroll事件就好了,没有任何兼容性差异。

dom.addEventListener('scroll', function () {
    console.log('元素滚动触发,滚动高度是:' + this.scrollTop);
});

高度获取则使用 clientHeight ,因为滚动的内容是不包括 border-box 的:

dom.addEventListener('scroll', function () {
    if (this.scrollTop > this.clientHeight) {
        console.log('滚动超过一屏了');
    }
});

对于普通元素的滚动高度获取,还有很多其他的原生API,例如 offsetHeight ,包含 border 边框大小; scrollHeight 包括滚动高度;getBoundingClientRect().height也是高度获取,不会可以是小数,特殊场景挺有用的。

三、答题核心概要总结

  1. 窗体滚动使用window.addEventListener,document有人反馈不反应;
  2. 窗体滚动滚动高度获取:window.pageYoffset(IE9+),
    document.documentElement.scrollTop(PC),document.body.scrollTop(Mobile);
  3. 普通元素直接scrollTop;
  4. 窗体高度获取:window.innerHeight(IE9+),
    备选方法为:document.documentElement.clientHeight;
  5. 普通元素高度获取:本题滚动事件中使用clientHeight(不含边框,滚动是在border-box里面的),
    offsetHeight包含边框,但是是整数;
    getBoundingClientRect().height也包含边框,可是是小数。(所有这几个高度相关API都兼容IE6+)

重要参考文档

本文提到的pageYoffset,innerHeight,clientHeight,offsetHeight,getBoundingClientRect等原生API都属于CSSOM视图模式(CSSOM View Module)相关内容,本文这里只是一部分,更多内容可以参见本站经典文章:“ CSSOM视图模式(CSSOM View Module)相关整理 ”。

下次直播预告

下周三群里发布CSS小测第2期正常,但是由于周六上班,有恰逢春节,因此直播答疑推迟到年后。想加入我的粉丝群的可以加我微信好友 zhangxinxu-job,我拉你们进去,备注“入群”,然后附上你们的姓名,方便我备注。

最后,祝广大学友新年快乐,逢胸化吉。

aIJNnuF.jpg!web

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。

本文地址: https://www.zhangxinxu.com/wordpress/?p=8425

(本篇完)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK