Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术随笔

    • git教程
    • 前端模块化:CommonJS,AMD,CMD,ES6
    • 如何动态加载本地图片
    • GitHub自动部署脚本
    • React生命周期详解
    • 将父组件的props作为组件的初始state的几种方案
    • webpack基础入门
    • StackOver记录:如何在function中使用redux
    • React中使用onClick时的三种性能优化
    • 思考React状态管理
    • forEach/for..in/of
    • styled-components使用记录
    • 深入理解forEach与forof
    • Node技术架构
    • Redux、Mobx状态管理杂谈
    • JS中的类型检测
    • 如何判断图片是否在可视区域
      • 前言
      • 方案一:[github]HTML DOM: offset+scrollTop
      • 方案二:[github]HTML DOM: ele.getBoundingClientRect()
      • 方案三:IntersectionObserver API
      • 🚨 注意:position的设置
      • 兼容性说明:
      • 总结
      • 使用 lodash 进行优化
      • 参考资料
    • 如何使用图片的懒加载操作
    • JavaScript模块化:从CommonJS到AMD
    • git查看:修改用户名、密码
    • 保持数据Immutable的几种写法
    • 如何实现一个Promise.all
    • resolve(Promise对象)会多出两个微任务
    • todo_基于Mocha的测试驱动开发
    • 浏览器页面渲染机制
    • todo-响应式编程之数据劫持
    • todo-immer库的介绍
    • React-Hooks 基础原理解析
    • JS基本功
    • 函数闭包与this指针
    • 收集 DUMI 配置过程中遇到的问题
    • typescript 类型工具
    • 统一接口的导入导出
    • 网络安全:如何预防 XSS 攻击
    • 浏览器页面渲染机制【2023】
    • 跨域资源请求
    • 浏览器事件循环机制
    • 如何理解浏览器的 user agent 用户代理的含义?
  • 转载文章

  • Mac相关

  • 前端学习周报

  • 包管理工具

  • 技术随笔
  • 技术随笔
wangjiasheng
2022-01-23
目录

如何判断图片是否在可视区域

# 前言

在性能优化方面,实现图片懒加载的功能是一项必备的技能。在实现过程时,我们需要判断图片是否在可视区域。由于页面绘制的复杂性性,需要 Check if an element is visible in a scrollable container,而网上搜出的99.99%的答案都是 Check if an element is visible within window's viewport,我也去搜索了stackoverflow 里头的答案,高赞的解决方案很多,但是把这些函数拷贝到自己的 Demo 中却是始终不生效,于是就有了这篇文章:

首先约定本文涉及到的变量含义:

  • 可滚动的容器:container
  • 元素:element

# 方案一:[github]HTML DOM: offset+scrollTop

在搜索的过程中发现一个宝藏网站:https://htmldom.dev/ (opens new window)

这个网站里头罗列了几十种常见的DOM操作,没事可以多翻一翻学习,有机会一定要翻译成中文,放在github的网站里头。

滚动的三种状态:

The following functions return true if the ele element is visible in its scrollable container:

const isVisible = function (ele, container) {
    const eleTop =   ele.offsetTop;
    const eleBottom = eleTop + ele.clientHeight;

    const containerTop = container.scrollTop;
    const containerBottom = containerTop + container.clientHeight;

    return (
        // element 完全在container
        (eleTop >= containerTop && eleBottom <= containerBottom) ||
        // element 刚进入container
        (eleTop < containerTop && containerTop < eleBottom) ||
        // element 即将离开container
        (eleTop < containerBottom && containerBottom < eleBottom)
    );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

使用方法:

var container = document.querySelector('#container');
var ele = document.querySelector('#ele');
if (isVisible(ele,container)) {
    // Do something...
}
1
2
3
4
5

# 方案二:[github]HTML DOM: ele.getBoundingClientRect()

还有一个函数推荐的比较多element.getBoundingClientRect(),与上面的offset定位的方式不同的是,使用时不需要对postion的定位方式做要求,根据实验可得:

  • element.getBoundingClientRect():元素相对于windows的Viewport的距离。

  • client.getBoundingClientRect():包在元素外层的container到Viewport的距离。

因此,一般来说,client 要小于element的距离。

注解:虽然网上推荐此函数比较多,但是大部分的场景还是在判断element是否在哎window的viewport可视区域,而如果要用于识别是否在container可视区域,就需要将两者相减,即将两个scrollTop相减后与element本身的height相比。

如果需要画图说明的话,还是使用relative比较清楚。

但是这里需要注意一下,bottom和right的的参考和传统的定位有所区别,始终以上边框和左边框作为基准线:

image-20220123210536248
const { bottom, height, top1 } = ele.getBoundingClientRect();
const { top2, bottom2} = container.getBoundingClientRect();
// 这里用一个三元表达式,将上面的滚动的三种方式已经囊括在里头了。
return top1 <= top2 ? top2 - top1 <= height : bottom1 - bottom2 <= height;
1
2
3
4

# 方案三:IntersectionObserver API

上面两种方案都使用到了**window.scroll 监听 Element.getBoundingClientRect() 并使用 _.throttle 节流**,浏览器其实已经内置了一个监听器完成这些操作了,可以通过读取changes[item]的intersetionRatio和isIntersecting属性判断是否元素重叠。

 this.observer = new IntersectionObserver((changes) => {
   // changes: 目标元素集合(需要取一下才可以)
   console.log('intersectionRatio', changes[0].intersectionRatio);
   console.log('isIntersecting', changes[0].isIntersecting);
});
1
2
3
4
5

# 🚨 注意:position的设置

在调试以上代码的时候,调试至少花费了有4个小时,总是被几个非常基础的问题给卡住,主要开在的是css属性的设置上,尤其是offset的取值上,由于没有考虑到position的设置,offset总是计算element 到 windows 视图窗的高度。

将position设置为relative的前提条件,也是在stackoverflow (opens new window)另一个问题的解答中提到的,如果它不提估计我又要卡好久,感谢感谢,stackoverflow真yyds!

image-20220123204911703

# 兼容性说明:

不同浏览器在获取window的可视高度方法是不同的,考虑到兼容性,常可以看到以下写法:

let vieportWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth)
let vieportHeight = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight)
1
2

还有一个例子,就是当需要获取页面的滚动高度scrollTop时,经常写成下面这段代码:

var scrollTop = window.pageYOffset  // 兼容 FireFox
                || document.documentElement.scrollTop  
                || document.body.scrollTop  
                || 0;
1
2
3
4

在 IE 和 Firefox 中:

  1. 当页面指定 DOCTYPE(DTD) 时,使用 document.documentElement。
  2. 当页面没有指定 DOCTYPE(DTD) 时,使用 document.body。

# 总结

  1. scrollHeight:只有container才有scroll相关属性,选中element无效果。

  2. scrollTop:一般情况下为0,只有滚动起来才可会有值,

  3. 【滚动时:不变】offsetTop:其值取决于container上设置的position值。

    • 如果position:static,返回的是element到windows的距离

    • 如果position:relative,返回的是element到container的距离。

# 使用 lodash 进行优化

import _ from "lodash"
class Example extends Component {
    constructor(props) {
        super(props);
        // 加入 防抖 操作
        this.isVisible3 = _.throttle(this.isVisible2,1000)
    }
  	isVisible = (container, ele) => {
        const eleTop = ele.offsetTop;
        const eleBottom = eleTop + ele.clientHeight;
        const containerTop = container.scrollTop;
        const containerBottom = containerTop + container.clientHeight;
        return (
            (eleTop >= containerTop && eleBottom <= containerBottom) ||
            (eleTop < containerTop && containerTop < eleBottom) ||
            (eleTop < containerBottom && containerBottom < eleBottom)
        );
    };
    isVisible2 = (container, ele) => {
        const { bottom, height, top } = ele.getBoundingClientRect();
        const containerRect = container.getBoundingClientRect();
        return top <= containerRect.top ? containerRect.top - top <= height : bottom - containerRect.bottom <= height;
    };
    componentDidMount() {
          const container = document.querySelector(".container")
          const ele = document.getElementById("ele")
          oContainer.addEventListener('scroll', () => {
              console.log(this.isVisible3(oContainer, ele))
          })

      }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 参考资料

  1. Github:HTML DOM (opens new window)

  2. How to test if an element is in the viewport with vanilla JavaScript (opens new window)

  3. 【Check If an Element is Visible in the Viewpor】 (opens new window)

    2,3两篇文章说的是一个内容,写的都挺不错的。

  4. 国内博客:JavaScript如何判断一个元素是否在可视区域中? (opens new window)

  5. 山月博客:网站开发中,如何实现图片的懒加载 (opens new window)

编辑 (opens new window)
上次更新: 2022/04/06, 15:04:00
JS中的类型检测
如何使用图片的懒加载操作

← JS中的类型检测 如何使用图片的懒加载操作→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式