个性签名: 生如夏花,逝如冬雪;人生如此,何悔何怨。

前言: 经常需要计算元素的大小或者所在页面的位置,offsetWidth,clientWidth,scrollWidth,scrollTop这几个关键字的出现更是家常便饭,每次碰到都需要事先实验一番。为了下次开发提高效率。在这里一次性做个总结,以用来判断元素是否在可视区域以及用原生js简单实现懒加载。文末有个简单的懒加载实现的demo,有需要的可以看一下。

目录

工欲善其事,必先利其器。在判断元素是否在可视区域实现简单的原生懒加载前,我们先简单回顾下以下几个关键的概念。

ps: 如果你对这些概念已经比较熟悉了,可以直接跳到第五点查看关键代码示例。

1. 偏移量

偏移量(offset dimension),元素的可见大小由其高度、宽度决定,包括所有内边距、滚动条和边框大小(注意,不包括外边距)。通过下列4个属性可以取得元素的偏移量。

偏移量 概念 公式
offsetHeight 元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的) 水平滚动条的高度、上边框高度和下边框高度。 offsetHeght = content + padding + border + scrollX
offsetWidth 元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂 直滚动条的宽度、左边框宽度和右边框宽度。 offsetWidth = content + padding + border + scrollY
offsetLeft 元素的左外边框至**包含元素的左内边框之间的像素距离。
offsetTop 元素的上外边框至包含元素的上内边框之间的像素距离。

其中,offsetLeft 和 offsetTop 属性与包含元素有关,包含元素的引用保存在 offsetParent 属性中。**offsetParent 属性不一定与 parentNode 的值相等。

如下图显示

注意: 所有这些偏移量属性都是只读的,而且每次访问它们都需要重新计算。因此,应该尽量避免重复访问这些属性;如果需要重复使用其中某些属性的值,可以将它们保 存在局部变量中,以提高性能。

这也是上篇文章文字跑马灯项目中(戳此跳转),为什么增加padding后,textWidth需要重新获取的原因

小结

偏移量: 只读属性;包括滚动条和边框,不包括外边距。

2. 客户区大小

客户区大小是只读的,每次访问都要重新计算的。

客户区大小 概念 公式
clientWidth clientWidth 属性是元素内容区宽度加 上左右内边距宽度; clientWidth = content + padding
clientHeight 元素内容区高度加上上下内边距高度 clientHeight = content + padding

最常用到这些属性的情况,就是确定浏览器视口大小的时候(在 IE7 之前的版本中)。如下面的例子所示:

function getViewport(){
// 检查 document.compatMode 属性,以确定浏览器是否运行在混杂模式。
// Safari3.1 之前的版本不支持这个属性,因此就会自动执行 else 语句
if (document.compatMode == "BackCompat"){
return {
width: document.body.clientWidth,
height: document.body.clientHeight
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
}
}

小结

客户区大小: 只读属性;不包括滚动条和边框,不包括外边距。

3. 滚动大小

概念
scrollHeight 在没有滚动条的情况下,元素内容的总高度。
scrollWidth 在没有滚动条的情况下,元素内容的总宽度。
scrollLeft 被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
scrollTop 被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。

scrollWidth 和 scrollHeight 主要用于确定元素内容的实际大小。

scrollLeft 和 scrollTop属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位 置。在元素尚未被滚动时,这两个属性的值都等于 0。如果元素被垂直滚动了,那么 scrollTop 的值 会大于 0,且表示元素上方不可见内容的像素高度。如果元素被水平滚动了,那么 scrollLeft 的值会 大于 0,且表示元素左侧不可见内容的像素宽度。这两个属性都是可以设置的,因此将元素的 scrollLeft 和 scrollTop 设置为 0,就可以重置元素的滚动位置。比如:上篇文章文字跑马灯项目中scrollLeft的使用(戳此跳转)

小结

只读属性,不包括滚动条、border。

4. 确定元素大小

getBoundingClientRect

getBoundingClientRect的兼容性写法:

对于不支持 getBoundingClientRect()的浏览器,可以通过其他手段取得相同的信息。一般来 说,right 和 left 的差值与 offsetWidth 的值相等,而 bottom 和 top 的差值与 offsetHeight 相等。综合上述,就可以创建出下面这个跨浏览器的函数:


function getElementLeft(element){
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null){
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
} function getElementTop(element){
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null){
actualTop += current. offsetTop;
current = current.offsetParent;
}
return actualTop;
} function getBoundingClientRect(element) {
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
if (element.getBoundingClientRect) {
if (typeof arguments.callee.offset != "number") {
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;"; document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop; document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
} else {
var actualLeft = getElementLeft(element);
var actualTop = getElementTop(element);
return {
left: actualLeft - scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
}
}
}

5.判断元素是否在可视区域

知道了元素的大小以及所位于的区域外,我们可以做些什么呢?我们可以通过上面学到的知识点来检测元素是否在可视区域,再说大一点,这也是懒加载图片的实现原理。

5.1 第一种方法

公式: el.offsetTop - document.documentElement.scrollTop <= viewPortHeight

function isInViewPortOfOne (el) {
// viewPortHeight 兼容所有浏览器写法
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
const offsetTop = el.offsetTop
const scrollTop = document.documentElement.scrollTop
const top = offsetTop - scrollTop
console.log('top', top)
// 这里有个+100是为了提前加载+ 100
return top <= viewPortHeight + 100
}

5.2 第二种方法

公式: el.getBoundingClientReact().top <= viewPortHeight

其实, el.offsetTop - document.documentElement.scrollTop = el.getBoundingClientRect().top, 利用这点,我们可以用下面代码代替方法一

function isInViewPortOfTwo (el) {
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
const top = el.getBoundingClientRect() && el.getBoundingClientRect().top
console.log('top', top)
return top <= viewPortHeight + 100
}

5.3 第三种方法

公式: intersectionRatio > 0 && intersectionRatio <= 1

// 定义一个交叉观察器
const io = new IntersectionObserver(ioes => {
ioes.forEach(ioe => {
const el = ioe.target
const intersectionRatio = ioe.intersectionRatio
if (intersectionRatio > 0 && intersectionRatio <= 1) {
loadImg(el)
io.unobserve(el)
}
el.onload = el.onerror = () => io.unobserve(el)
})
})
// 执行交叉观察器
function isInViewPortOfThree (el) {
io.observe(el)
}

5.4 兼容性比较

在兼容性方面,我们知道越原始的方法兼容性是最好的,那么第二种方法和第三种方法能否代替第三种方法呢?我们来看看。

从caniuse的数据来看,getBoundingClientReact的适配情况很乐观了。

所以,如果在移动端和桌面端都要做兼容适配的话,方法二完全可以代替方法一进行适配了。如果仅仅是桌面端适配(比如运营后台),我们或许可以尝试下新的IntersectionObserver方法,毕竟IntersectionObserver里面还有更多丰富的功能等着我们去体验呢。

5.5 实例

有时,我们希望某些静态资源(比如图片),只有用户向下滚动,它们进入视口时才加载,这样可以节省带宽,提高网页性能。这就叫做"惰性加载",也称为懒加载。

惰性加载预览DEMO(放入你的本地图片即可通过更换不同方法实现懒加载)

------------------------------ 华丽的分割线 ---------------------------

ps: 有公众号大佬想减轻日常写推文的时间消耗,收购技术原创文章的稿子放在自己公众号展示推送的,可以联系找我合作哈。(在公众号里可以查到我的联系方式)

关于我

参考

如何判断元素是否在可视区域ViewPort的更多相关文章

  1. 使用jQuery判断元素是否在可视区域

    $("#app").offset().top; offset().top表示 绝对偏移值,比如说有一个很长的页面,#app这个元素 在最底下,  $("#app" ...

  2. 如何判断元素是否在可视区域内--getBoundingClientRect

    介绍 Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置. 根据MDN文档 getBoundingClientRect 方法返回的是一个DOMRect ...

  3. Vue-懒加载(判断元素是否在可视区域内)

    上公式: 元素距离顶部高度(elOffsetTop) >= dom滚动高度(docScrollTop) 并且元素距离顶部高度(elOffsetTop) < (dom滚动高度 + 视窗高度) ...

  4. jq、js判断元素是否在可视区域内

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> ...

  5. javascript判断某种元素是否进入可视区域

    判断是否在指定的可视区域内,先用最简单的方式,比如整个页面为可视区域 找到几个关键因素: sTop= $(window).scrollTop();  //滚动条距顶部的高度 clientHeight= ...

  6. JS代码片段:判断一个元素是否进入可视区域

    // Determine if an element is in the visible viewport function isInViewport(element) { var rect = el ...

  7. 原生js获取 一个dom元素距离页面可视区域的位置值 -- getBoundingClientRect

    getBoundingClientRect() 这个方法返回一个矩形对象,包含四个属性:left.top.right和bottom.分别表示元素各边与页面上边和左边的距离. var box=docum ...

  8. jQuery 怎么判断DIV出现在可视区域

    直接上代码: $(window).scroll(function () { var oT = document.getElementById("myDiv").offsetTop; ...

  9. 如何判断一个Div是否可视区域,判断div是否可见

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

随机推荐

  1. 菜鸟系列docker——搭建私有仓库harbor(6)

    docker 搭建私有仓库harbor 1. 准备条件 安装docker sudo yum update sudo yum install -y yum-utils device-mapper-per ...

  2. POJ3450最长公共子串【kmp】

    题目链接:http://poj.org/problem?id=3450 题目大意:给定n个长度不超过200的字符串,n < 4000.求这些字符串的最长公共子串,若没有,则输出 “IDENTIT ...

  3. 并发-synchronized

    线程并发-synchronized和Lock简单认知 前几天刚加深了线程的了解,期间在验证各种方法及多线程时遇到一些疑问,在高并发的情况下,怎么做才能保证程序还能按照我们预期的正常运行下去,这就是我们 ...

  4. Find Duplicate File in System

    Given a list of directory info including directory path, and all the files with contents in this dir ...

  5. row_number()、rank()、dense_rank()排序方式的区别

    1.row_number() 排序策略,连续排序,它会为查询出来的每一行记录生成一个序号,依次排序且不会重复,例如1,2,3,4   SELECT names,dept,row_number() OV ...

  6. Nginx的一些常用配置

    #定义Nginx运行的用户和用户组 #user nobody; #nginx进程数,建议设置为等于CPU总核心数. worker_processes 1; #全局错误日志定义类型,[ debug | ...

  7. URL去重与文章去重的一些基本方法

    一.url去重url存到数据库所有url放到set中(一亿条占用9G内存)md5之后放到set中(一亿条占用2,3G的内存)scrapy采用的就是类似方法bitmap方法(url经过hash后映射到b ...

  8. JSONObject 的使用

    1. 导入依赖 这里以 20180813 的 json 版本为例 <dependency> <groupId>org.json</groupId> <arti ...

  9. 23-Perl 面向对象

    1.Perl 面向对象Perl 中有两种不同地面向对象编程的实现:一是基于匿名哈希表的方式,每个对象实例的实质就是一个指向匿名哈希表的引用.在这个匿名哈希表中,存储来所有的实例属性.二是基于数组的方式 ...

  10. Joomla 3.0.0 - 3.4.6 RCE漏洞分析记录

    0x00  前言 今天早上看到了国内几家安全媒体发了Joomla RCE漏洞的预警,漏洞利用的EXP也在Github公开了.我大致看了一眼描述,觉得是个挺有意思的漏洞,因此有了这篇分析的文章,其实这个 ...