热点面试题:JS 如何判断一个元素是否在可视区域内?

简介: 热点面试题: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)

相关文章
|
28天前
|
JavaScript 前端开发 程序员
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
|
1月前
|
JSON JavaScript 前端开发
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
本文介绍了JSONP的工作原理及其在解决跨域请求中的应用。首先解释了同源策略的概念,然后通过多个示例详细阐述了JSONP如何通过动态解释服务端返回的JavaScript脚本来实现跨域数据交互。文章还探讨了使用jQuery的`$.ajax`方法封装JSONP请求的方式,并提供了具体的代码示例。最后,通过一个更复杂的示例展示了如何处理JSON格式的响应数据。
36 2
[JS]面试官:你的简历上写着熟悉jsonp,那你说说它的底层逻辑是怎样的?
|
1月前
|
JavaScript 前端开发 开发者
.js的dom元素操作
【10月更文挑战第29天】通过灵活运用这些 DOM 元素操作方法,JavaScript 可以实现丰富的网页交互效果,如动态更新页面内容、响应用户操作、创建和删除页面元素等。在实际开发中,开发者可以根据具体的需求和场景,选择合适的 DOM 元素操作方法来实现所需的功能,为用户提供更加流畅和动态的网页体验。
|
3月前
|
存储 前端开发 JavaScript
前端基础(二)_JavaScript变量、JavaScript标识符、JavaScript获取元素、JavaScript的鼠标事件
本文介绍了JavaScript变量的声明和使用、标识符的命名规则、如何获取和操作HTML元素,以及JavaScript的鼠标事件处理,通过示例代码展示了这些基础知识点在实际开发中的应用。
49 2
前端基础(二)_JavaScript变量、JavaScript标识符、JavaScript获取元素、JavaScript的鼠标事件
|
2月前
|
移动开发 JavaScript 前端开发
原生js如何获取dom元素的自定义属性
原生js如何获取dom元素的自定义属性
79 4
|
2月前
|
JavaScript
js删除数组中已知下标的元素
js删除数组中已知下标的元素
45 4
|
2月前
|
JavaScript 前端开发 索引
JS 删除数组元素( 5种方法 )
JS 删除数组元素( 5种方法 )
62 1
|
3月前
|
JavaScript 前端开发
JavaScript HTML DOM 元素 (节点)
JavaScript HTML DOM 元素 (节点)
28 2
|
2月前
|
Web App开发 JavaScript 前端开发
前端Node.js面试题
前端Node.js面试题
|
3月前
|
JavaScript 前端开发
js删除数组最后一个元素
js删除数组最后一个元素