JavaScript如何判断一个元素是否在可视区域中?
一、用途
可视区域即我们浏览网页的设备肉眼可见的区域,如下图
在日常开发中,我们经常需要判断目标元素是否在视窗之内或者和视窗的距离小于一个值(例如 100 px),从而实现一些常用的功能,例如:
- 图片的懒加载
- 列表的无限滚动
- 计算广告元素的曝光情况
- 可点击链接的预加载
二、实现方式
判断一个元素是否在可视区域,我们常用的有三种办法:
offsetTop、scrollTop
getBoundingClientRect
Intersection Observer
offsetTop、scrollTop
offsetTop,元素的上外边框至包含元素的上内边框之间的像素距离,其他offset属性如下图所示:
下面再来了解下clientWidth、clientHeight:
clientWidth:元素内容区宽度加上左右内边距宽度,即clientWidth = content + paddingclientHeight:元素内容区高度加上上下内边距高度,即clientHeight = content + padding
这里可以看到client元素都不包括外边距
最后,关于scroll系列的属性如下:
scrollWidth和scrollHeight主要用于确定元素内容的实际大小scrollLeft和scrollTop属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位置- 垂直滚动
scrollTop > 0 - 水平滚动
scrollLeft > 0 将元素的
scrollLeft和scrollTop设置为 0,可以重置元素的滚动位置
注意
- 上述属性都是只读的,每次访问都要重新开始
下面再看看如何实现判断:
公式如下:
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
return top <= viewPortHeight
}
getBoundingClientRect
返回值是一个 DOMRect对象,拥有left, top, right, bottom, x, y, width, 和 height属性
const target = document.querySelector('.target');
const clientRect = target.getBoundingClientRect();
console.log(clientRect);
// {
// bottom: 556.21875,
// height: 393.59375,
// left: 333,
// right: 1017,
// top: 162.625,
// width: 684
// }
属性对应的关系图如下所示:
当页面发生滚动的时候,top与left属性值都会随之改变
如果一个元素在视窗之内的话,那么它一定满足下面四个条件:
- top 大于等于 0
- left 大于等于 0
- bottom 小于等于视窗高度
- right 小于等于视窗宽度
实现代码如下:
function isInViewPort(element) {
const viewWidth = window.innerWidth || document.documentElement.clientWidth;
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
const {
top,
right,
bottom,
left,
} = element.getBoundingClientRect();
return (
top >= 0 &&
left >= 0 &&
right <= viewWidth &&
bottom <= viewHeight
);
}
Intersection Observer
Intersection Observer 即重叠观察者,从这个命名就可以看出它用于判断两个元素是否重叠,因为不用进行事件的监听,性能方面相比getBoundingClientRect会好很多
使用步骤主要分为两步:创建观察者和传入被观察者
创建观察者
const options = {
// 表示重叠面积占被观察者的比例,从 0 - 1 取值,
// 1 表示完全被包含
threshold: 1.0,
root:document.querySelector('#scrollArea') // 必须是目标元素的父级元素
};
const callback = (entries, observer) => { ....}
const observer = new IntersectionObserver(callback, options);
通过new IntersectionObserver创建了观察者 observer,传入的参数 callback 在重叠比例超过 threshold 时会被执行`
关于callback回调函数常用属性如下:
// 上段代码中被省略的 callback
const callback = function(entries, observer) {
entries.forEach(entry => {
entry.time; // 触发的时间
entry.rootBounds; // 根元素的位置矩形,这种情况下为视窗位置
entry.boundingClientRect; // 被观察者的位置举行
entry.intersectionRect; // 重叠区域的位置矩形
entry.intersectionRatio; // 重叠区域占被观察者面积的比例(被观察者不是矩形时也按照矩形计算)
entry.target; // 被观察者
});
};
传入被观察者
通过 observer.observe(target) 这一行代码即可简单的注册被观察者
const target = document.querySelector('.target');
observer.observe(target);
三、案例分析
实现:创建了一个十万个节点的长列表,当节点滚入到视窗中时,背景就会从红色变为黄色
Html结构如下:
<div class="container"></div>
css样式如下:
.container {
display: flex;
flex-wrap: wrap;
}
.target {
margin: 5px;
width: 20px;
height: 20px;
background: red;
}
往container插入1000个元素
const $container = $(".container");
// 插入 100000 个 <div class="target"></div>
function createTargets() {
const htmlString = new Array(100000)
.fill('<div class="target"></div>')
.join("");
$container.html(htmlString);
}
这里,首先使用getBoundingClientRect方法进行判断元素是否在可视区域
function isInViewPort(element) {
const viewWidth = window.innerWidth || document.documentElement.clientWidth;
const viewHeight =
window.innerHeight || document.documentElement.clientHeight;
const { top, right, bottom, left } = element.getBoundingClientRect();
return top >= 0 && left >= 0 && right <= viewWidth && bottom <= viewHeight;
}
然后开始监听scroll事件,判断页面上哪些元素在可视区域中,如果在可视区域中则将背景颜色设置为yellow
$(window).on("scroll", () => {
console.log("scroll !");
$targets.each((index, element) => {
if (isInViewPort(element)) {
$(element).css("background-color", "yellow");
}
});
});
通过上述方式,可以看到可视区域颜色会变成黄色了,但是可以明显看到有卡顿的现象,原因在于我们绑定了scroll事件,scroll事件伴随了大量的计算,会造成资源方面的浪费
下面通过Intersection Observer的形式同样实现相同的功能
首先创建一个观察者
const observer = new IntersectionObserver(getYellow, { threshold: 1.0 });
getYellow回调函数实现对背景颜色改变,如下:
function getYellow(entries, observer) {
entries.forEach(entry => {
$(entry.target).css("background-color", "yellow");
});
}
最后传入观察者,即.target元素
$targets.each((index, element) => {
observer.observe(element);
});
可以看到功能同样完成,并且页面不会出现卡顿的情况
JavaScript如何判断一个元素是否在可视区域中?的更多相关文章
- JS代码片段:判断一个元素是否进入可视区域
// Determine if an element is in the visible viewport function isInViewport(element) { var rect = el ...
- javascript判断某种元素是否进入可视区域
判断是否在指定的可视区域内,先用最简单的方式,比如整个页面为可视区域 找到几个关键因素: sTop= $(window).scrollTop(); //滚动条距顶部的高度 clientHeight= ...
- iOS 如何判断一个点在某个指定区域中
在iOS 开发中会遇到 判断位置的情况 iOS 自己都有函数实现的这些功能. 判断一个点是否在这个rect区域中 bool CGRectContainsPoint(CGRect rect,CGPoin ...
- Python3基础 not in列表名 判断一个元素是否不在列表中列表中
镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...
- 如何判断一个Div是否在可视区域,判断div是否可见
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- js 判断一个元素是否在滚动的可视区域内,不在就固定到可视区域的上方。
前言:最近工作中,有这样一个场景,判断一个元素是否在滚动的可视区域内,不在就固定到可视区域的上方.为了以后再次遇到,所以记录下来,并分享.转载请注明出处:https://www.cnblogs.com ...
- javascript判断一个元素是另外一个元素的子元素
javascript判断一个元素是另外一个元素的子元素用途有很多,最常用的就是当点击页面的空白处去执行某些操作,比如弹出层等. function isParent (obj,parentObj){ w ...
- 如何判断元素是否在可视区域ViewPort
个性签名: 生如夏花,逝如冬雪:人生如此,何悔何怨. 前言: 经常需要计算元素的大小或者所在页面的位置,offsetWidth,clientWidth,scrollWidth,scrollTop这几个 ...
- jquery and js 判断一个元素是否存在
一.javascript中判断一个元素是否存在 if(document.getElementById('example')){ // do sth } 二.jquery中判断一个元素是否存在 < ...
- jQuery判断一个元素是否为另一个元素的子元素(或者其本身)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head& ...
随机推荐
- 【数据结构】C语言实现动态扩容数组
// resizable array /* Think about a set of functions that provide a mechanism of resizable array of ...
- AAC编解码移植之基本简介
一 概念 AAC是高级音频编码(Advanced Audio Coding)的缩写,出现于1997年,最初是基于MPEG-2的音频编码技术.由Fraunhofer IIS.Dolby Laborato ...
- python 读取txt并绘制波形图实例解析
一 用python绘图有很多方法,笔者找到了一种最简单的方法,使用非常便利,这里分享一下: import numpy as np import matplotlib.pyplot as plt a = ...
- Android Material组件库(日期选择和时间选择器)基本使用
原文:Android Material组件库(日期选择和时间选择器)基本使用 - Stars-One的杂货小窝 简单的封装下Material组件里的日期选择器和时间选择器的使用方法 效果 代码 需要添 ...
- CentOS 7.9 环境下搭建k8s集群(一主两从)
目录 一.硬件准备(虚拟主机) 二.环境准备 1.所有机器关闭防火墙 2.所有机器关闭selinux 3.所有机器关闭swap 4.所有机器上添加主机名与ip的对应关系 5.在所有主机上将桥接的ipv ...
- day06-Java流程控制
Java流程控制 1.用户交互Scanner java.util.Scanner是Java5的新特征,我们可以通过Scannner类来获取用户的输入. 基本语法: Scanner s = new Sc ...
- 一个简单的RTMP服务器实现 --- RTMP与H264
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- ubuntu 14.04 x86_64编译安装Skyeye1.3.5RC1的心得
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文发布于 2014-11-09 09:19:03 ...
- TomCat 的 Jenkins 报错:反向代理设置有误
1.进入 Linux 系统的 TomCat 安装目录的 conf 目录 2.编辑 server.xml 3.找到 <Connector> 标签 4.这里的 redirectPort 的值才 ...
- apache添加php模块
实验介绍: apache本身只能发布静态网站,而添加了php模块就可以发布动态网站 一:下载php 进入php官方网址https://www.php.net/ 点击进入windows版本 下载thre ...