热点面试题:JS 如何判断一个元素是否在可视区域内?
前言
系列首发于公众号『前端进阶圈』 ,更多精彩内容敬请关注公众号最新消息。
JS 如何判断一个元素是否在可视区域内?
- 方法一:
offsetTop、scrollTop
- 方法二:
getBoundingClientRect()
- 方法三:
Intersection Observer
方法一:offsetTop、scrollTop
// 公式
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
// }
// A:
// 如果一个元素在视窗之内的话,那么它一定满足下面四个条件:
// 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
const callback = function(entries, observer) {
entries.forEach(entry => {
entry.time; // 触发的时间
entry.rootBounds; // 根元素的位置矩形,这种情况下为视窗位置
entry.boundingClientRect; // 被观察者的位置举行
entry.intersectionRect; // 重叠区域的位置矩形
entry.intersectionRatio; // 重叠区域占被观察者面积的比例(被观察者不是矩形时也按照矩形计算)
entry.target; // 被观察者
});
};
// 第二步:传入被观察者
const target = document.querySelector('.target');
observer.observe(target);
完整代码
前两种方法
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <title>Document</title> <style> .container { display: flex; flex-wrap: wrap; } .target { margin: 5px; width: 20px; height: 20px; background: red; } </style> </head> <body> <div class="container"></div> </body> </html> <script> (() => { const $container = $(".container"); function createTargets() { const htmlString = new Array(10000).fill('<div class="target"></div>').join("") $container.html(htmlString) } createTargets(); const $targets = $(".target"); function isInViewPort(el){ //方法1 // const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; // const offsetTop = el.offsetTop; // const scollTop = document.documentElement.scrollTop; // return offsetTop-scollTop <= viewPortHeight // 方法2 const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; const viewPortWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; const {top,right,left,bottom} = el.getBoundingClientRect(); return top >= 0 && left >= 0 && bottom <= viewPortHeight && right <= viewPortWidth } //事件监听 $(window).on("scroll",()=>{ console.log("scroll!!"); $targets.each((index,element)=>{ if(isInViewPort(element)){ $(element).css("background-color","blue") } }) }) })(); </script>
第三种方法
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <title>Document</title> <style> * { margin: 0; padding: 0; } .test { width: 200px; height: 1000px; background: orange; } .box { width: 150px; height: 150px; margin: 50px; background: red; } #sta { position: fixed; left: 40%; top: 40%; width: 200px; height: 100px; background: greenyellow; } </style> </head> <body> <div class="test">test</div> <div class="box">box</div> <div id="sta">初始化</div> </body> </html> <script> (() => { var status_node=document.querySelector("#sta"); const box = document.querySelector('.box'); const intersectionObserver = new IntersectionObserver((entries) => { entries.forEach((item) => { if (item.isIntersecting) { box.innerText = '进入可视区域'; status_node.innerText = '进入可视区域'; console.log('进入可视区域'); }else{ box.innerText = '出去了'; status_node.innerText = '出去了'; } }) }); intersectionObserver.observe(box); })(); </script>
特殊字符描述:
- 问题标注
Q:(question)
- 答案标注
R:(result)
- 注意事项标准:
A:(attention matters)
- 详情描述标注:
D:(detail info)
- 总结标注:
S:(summary)
- 分析标注:
Ana:(analysis)
提示标注:
T:(tips)
往期推荐:
- 热点面试题:浏览器和Node的宏任务和微任务?
- 这是你理解的CSS选择器权重吗?
- 热点面试题:JS 中 call, apply, bind 概念、用法、区别及实现?
- 热点面试题: 常用位运算方法?
- Vue数据监听Object.definedProperty()方法的实现
- 热点面试题:Virtual DOM 相关问题?
- 热点面试题: Array中有哪些非破坏性方法?
最后:
- 欢迎关注 『前端进阶圈』 公众号 ,一起探索学习前端技术......
- 公众号回复 加群 或 扫码, 即可加入前端交流学习群,一起快乐摸鱼和学习......
- 公众号回复 加好友,即可添加为好友
47 声望
2 粉丝
推荐阅读
JavaScript深度剖析之变量、函数提升:从表面到本质
JavaScript深度剖析之变量、函数提升:从表面到本质前言系列首发于公众号『前端进阶圈』 ,若不想错过更多精彩内容,请“星标”一下,敬请关注公众号最新消息。想要彻底理解提升这篇文章,除非你已经理解了作用域、...
控心crazy
安全地在前后端之间传输数据 - 「3」真的安全吗?
在「2」注册和登录示例中,我们通过非对称加密算法实现了浏览器和 Web 服务器之间的安全传输。看起来一切都很美好,但是危险就在哪里,有些人发现了,有些人嗅到了,更多人却浑然不知。就像是给门上了把好锁,还...
边城赞 31阅读 7.2k评论 5
涨姿势了,有意思的气泡 Loading 效果
今日,群友提问,如何实现这么一个 Loading 效果:这个确实有点意思,但是这是 CSS 能够完成的?没错,这个效果中的核心气泡效果,其实借助 CSS 中的滤镜,能够比较轻松的实现,就是所需的元素可能多点。参考我们...
chokcoco赞 20阅读 2.1k评论 2
在前端使用 JS 进行分类汇总
最近遇到一些同学在问 JS 中进行数据统计的问题。虽然数据统计一般会在数据库中进行,但是后端遇到需要使用程序来进行统计的情况也非常多。.NET 就为了对内存数据和数据库数据进行统一地数据处理,发明了 LINQ (L...
边城赞 17阅读 1.9k
【已结束】SegmentFault 思否写作挑战赛!
SegmentFault 思否写作挑战赛 是思否社区新上线的系列社区活动在 2 月 8 日 正式面向社区所有用户开启;挑战赛中包含多个可供作者选择的热门技术方向,根据挑战难度分为多个等级,快来参与挑战,向更好的自己前进!
SegmentFault思否赞 20阅读 5.6k评论 10
过滤/筛选树节点
又是树,是我跟树杠上了吗?—— 不,是树的问题太多了!🔗 相关文章推荐:使用递归遍历并转换树形数据(以 TypeScript 为例)从列表生成树 (JavaScript/TypeScript) 过滤和筛选是一个意思,都是 filter。对于列表来...
边城赞 18阅读 7.7k评论 3
你可能不需要JS!CSS实现一个计时器
CSS现在可不仅仅只是改一个颜色这么简单,还可以做很多交互,比如做一个功能齐全的计时器?样式上并不复杂,主要是几个交互的地方数字时钟的变化开始、暂停操作重置操作如何仅使用 CSS 来实现这样的功能呢?一起...
XboxYan赞 21阅读 1.6k评论 1
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。