判断当前元素是否在可视区域

951 阅读1分钟

前提

大家在开发页面的时候肯定有一些操作,就是对可视区域的元素的一个处理,我这边呢就有对可视区的列表进行埋点的处理,以下是我总结的两个方法

有两个方法:

方法一:

判断每个元素是否在可视区域,使用每个元素的高度,以及距离头部的距离以及可视区域的高度

注意

  • 最外层的盒子需要绑定滚动事件,切高度和宽度必须和可视区域的大小一致

  • 里面嵌套的盒子需要由滚动的元素自己撑开

  • 判断是否在可是区域的条件(需要进行一下反向思维)

  • 判断是否在可是区域的上面(元素距离父元素的距离 + 自己本身的高度 < 页面滚动的距离)

  • 判断是否在可是区域的下面(元素距离父元素的距离 > 页面滚动的距离+可视区高度的距离)

  • 上面的判断是不在可是区域,非一下就是在可是区域

代码如下

<!DOCTYPE html>
<html lang="en">
<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">
    <title>复杂版本的可是区域展示</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            list-style: none;
            text-decoration: none;
        }
        .wrap {
            width: 100%;
            height: 100vh;
            overflow-y: scroll;
        }
        ul li {
            width: 100%;
            height: 100vh;
            text-align: center;
            line-height: 100vh;
            font-size: 50px;
        }
    </style>
</head>
<body>
    <div class="wrap" onscroll="scrollfun(event)">
        <ul>
            <li style="background: red;" issend="false">1</li>
            <li style="background: yellow;" issend="false">2</li>
            <li style="background: green;" issend="false">3</li>
            <li style="background: gray;" issend="false">4</li>
            <li style="background: pink;" issend="false">5</li>
        </ul>
    </div>
    <script>
        let lis = document.querySelectorAll('li'),
            wrap = document.querySelector('.wrap');
        window.scrollfun = (e) => {
            lis.forEach((i, ind) => {
                if (isElementInViewport(i)) {
                    i.setAttribute("issend", "true")
                } else {
                    i.setAttribute("issend", "false")
                }
            })
        }
        /**
          * @description: 判断dom元素是否在可视区域内*
          * @param: el: Vuecomponnet对象*
          * @returns: true-在可视区域,false: 不在可视区域
        **/
         const isElementInViewport = function (el) {
            let s = el.offsetTop // 元素相对于页面顶部的距离
            let x = el.offsetHeight //元素自身高度
            let t = wrap.scrollTop // 页面在垂直方向上滚动的距离
            let y = window.innerHeight //窗口可视区域的高度
            // (s + x) < t 这个指的是可视区域上面的移出的元素
            // (s > (t + y)) 这个指的是可视区域下面还没有移入的元素
            let isHidden = (s + x) < t || (s > (t + y));
            return !isHidden
        };
    </script>
</body>
</html>

方法二:

使用web API 提供的方法IntersectionObserver,可以方便快捷的判断出是否在可视区域

注意:

  • 最外层的盒子高度和可视区域的高度一样

  • 每个元素调用次方法,判断是否在可视区域

  • 必须实例化 IntersectionObserver 对象,以及调用 observe 的方法

  • 可视区域的判断这次是对元素的位置头部和底部的位置是否在可视区域的高度以内

代码如下

<!DOCTYPE html>
<html lang="en">
<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">
    <title>复杂版本的可是区域展示</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            list-style: none;
            text-decoration: none;
        }
        .wrap {
            width: 100%;
            height: 100vh;
            overflow-y: scroll;
        }
        ul li {
            width: 100%;
            height: 100vh;
            text-align: center;
            line-height: 100vh;
            font-size: 50px;
        }
    </style>
</head>
<body>
    <div class="wrap">
        <ul>
            <li style="background: red;" issend="false">1</li>
            <li style="background: yellow;" issend="false">2</li>
            <li style="background: green;" issend="false">3</li>
            <li style="background: gray;" issend="false">4</li>
            <li style="background: pink;" issend="false">5</li>
        </ul>
    </div>
    <script>
        let wrapel = document.querySelector('.wrap'),
            lis = document.querySelectorAll('li');
        lis.forEach((i, ind) => {
            startinit(i, ind)
        })
        /**
         *  初始化执行这个是否在可是区域的函数初始化
         *  @param {object} el 这个表示的是页面的节点
         *  @param {string} type 这个表示的是页面中的一个type的类型处理
         *  @returns {void}
         * */
        function startinit(el, ind) {
            let isnotsend = false;
            let intersectionObserver = new IntersectionObserver((entries) => {
                if (isnotsend) return;
                isnotsend = true;
                // entries[0].boundingClientRect.top 目标元素el距离可视区域头部的位置
                // entries[0].boundingClientRect.bottom 目标元素el距离可视区域底部的位置
                // wrapel.offsetHeight 可视区域的高度
                if ((entries[0].boundingClientRect.top <= wrapel.offsetHeight && entries[0].boundingClientRect
                        .top > 0) || (entries[0].boundingClientRect.bottom > 0 && entries[0].boundingClientRect
                        .bottom <= wrapel.offsetHeight)) {
                    entries[0].target.setAttribute('issend', 'true')
                } else {
                    entries[0].target.setAttribute('issend', 'false')
                }
                isnotsend = false;
            });
            intersectionObserver.observe(el);
        }
    </script>
</body>
</html>