js 可视区域的判断

之前看 vuetifyjs 文档,然后看到一个【交叉观察者】,觉得很有意思,我就在想,一个dom元素,在页面中,我们怎么判断它是在可视区域内呢?

1. 方法1:getBoundingClientRect

let domRect = dom.getBoundingClientRect();

 

 DOMRect:{

  x/left:视图原点(左上角)距离dom左边框距离,

  y/top:视图原点(左上角)距离dom上边框距离,

       right:视图原点(左上角)距离dom右边框距离,

       bottom:视图原点(左上角)距离dom底边框距离,

       width:dom的宽度,标准盒模型,width = 宽度+padding+border;怪异盒模型,width = 设置的宽度,

  height:dom的高度,

}

所以我们可以根据DOMRect的中的各个属性来判断dom是否在可视区域内。

1.1 效果截图

                

1.2 代码示例:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 
 4 <head>
 5   <meta charset="UTF-8">
 6   <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7   <meta name="viewport" content="width=device-width, initial-scale=1.0">
 8   <title>getBoundingClientRect可视区域</title>
 9   <style>
10     * {
11       margin: 0
12     }
13     .circle-wrap {
14       position: fixed;
15       top: 50px;
16       right: 50px;
17       padding: 10px;
18       box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
19       width: 140px;
20       background: #fff;
21     }
22 
23     .circle-wrap .circle {
24       display: inline-block;
25       width: 20px;
26       height: 20px;
27       border-radius: 50%;
28       background: red;
29     }
30 
31     .card-wrap {
32       height: 2000px;
33       width: 3000px;
34       margin-top: 100px;
35     }
36 
37     .card-wrap .card {
38       width: 200px;
39       height: 200px;
40       padding: 20px;
41       box-sizing: border-box;
42       box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
43       margin-top: 500px;
44       margin-left: 500px;
45     }
46   </style>
47 </head>
48 
49 <body>
50   <div class="circle-wrap">
51     <span>当下方的卡片在可视区域内,我的圈圈是绿色,否则是红色</span>
52     <span class="circle"></span>
53   </div>
54   <div class="card-wrap">
55     <div class="card">我是一个卡片</div>
56   </div>
57   <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.js"></script>
58   <script>
59     const card = document.querySelector(".card");
60     const circle = document.querySelector(".circle");
61     const getBoundingClientRectJudge = () => {
62       let domRect = card.getBoundingClientRect();
63       console.log(domRect)
64       let ch = document.documentElement.clientHeight;
65       let cw = document.documentElement.clientWidth;
66       let isInsert = true;
67       if (domRect.bottom < 0 || domRect.top > ch || domRect.right < 0 || domRect.left > cw) {
68         isInsert = false;
69       }
70       let background = null
71       if (isInsert) {
72         background = "green"
73       } else {
74         background = "red"
75       }
76       circle.style.background = background
77     }
78     window.addEventListener("scroll", _.throttle(getBoundingClientRectJudge, 500))
79     getBoundingClientRectJudge()
80   </script>
81 </body>
82 
83 </html>

1.3 问题

getBoundingClientRect并不能满足所有情况,甚至说,它只能满足一种情况的判断,那就是需要判断的那个dom节点,它只身处在一个滚动条的情况下。什么意思呢,就是它所有的祖先节点,加起来的滚动条只有1个,如果它父节点有滚动条,父父节点也有滚动条,那这种方法就G了,请看示例:
下面展示的结果是错误的,圈圈应该是红色才对。
代码示例:
  1 <!DOCTYPE html>
  2 <html lang="en">
  3 
  4 <head>
  5   <meta charset="UTF-8">
  6   <meta http-equiv="X-UA-Compatible" content="IE=edge">
  7   <meta name="viewport" content="width=device-width, initial-scale=1.0">
  8   <title>getBoundingClientRect可视区域</title>
  9   <style>
 10     * {
 11       margin: 0
 12     }
 13 
 14     body {
 15       height: 2000px;
 16       width: 3000px;
 17     }
 18 
 19     .circle-wrap {
 20       position: fixed;
 21       top: 50px;
 22       right: 50px;
 23       padding: 10px;
 24       box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
 25       width: 140px;
 26       background: #fff;
 27     }
 28 
 29     .circle-wrap .circle {
 30       display: inline-block;
 31       width: 20px;
 32       height: 20px;
 33       border-radius: 50%;
 34       background: red;
 35     }
 36 
 37     .card-wrap {
 38       height: 400px;
 39       width: 600px;
 40       margin-top: 100px;
 41       margin-left: 100px;
 42       border: 1px solid green;
 43       overflow: auto;
 44     }
 45 
 46     .card-wrap .card {
 47       width: 200px;
 48       height: 200px;
 49       padding: 20px;
 50       box-sizing: border-box;
 51       box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
 52       margin-top: 500px;
 53       margin-left: 500px;
 54     }
 55     .head{
 56       width: 100%;
 57       height: 100px;
 58       background: pink;
 59       /* position: fixed; */
 60       top:0;
 61       left: 0;
 62     }
 63   </style>
 64 </head>
 65 
 66 <body>
 67   <!-- <div class="head"></div> -->
 68   <div class="circle-wrap">
 69     <span>当下方的卡片在可视区域内,我的圈圈是绿色,否则是红色</span>
 70     <span class="circle"></span>
 71   </div>
 72   <div class="card-wrap">
 73     <div class="card">我是一个卡片</div>
 74   </div>
 75   <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.js"></script>
 76   <script>
 77     const card = document.querySelector(".card");
 78     const circle = document.querySelector(".circle");
 79     const getBoundingClientRectJudge = () => {
 80       let domRect = card.getBoundingClientRect();
 81       let ch = document.documentElement.clientHeight;
 82       let cw = document.documentElement.clientWidth;
 83       let isInsert = true;
 84       if (domRect.bottom < 0 || domRect.top > ch || domRect.right < 0 || domRect.left > cw) {
 85         isInsert = false;
 86       }
 87       let background = null
 88       if (isInsert) {
 89         background = "green"
 90       } else {
 91         background = "red"
 92       }
 93       circle.style.background = background
 94     }
 95     window.addEventListener("scroll", _.throttle(getBoundingClientRectJudge, 500))
 96     getBoundingClientRectJudge()
 97   </script>
 98 </body>
 99 
100 </html>

 

2. 方法2:IntersectionObserver

 由此,我们引出第二种方法,IntersectionObserver。

2.1 效果截图

        

2.1 代码示例

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 
  4 <head>
  5   <meta charset="UTF-8">
  6   <meta http-equiv="X-UA-Compatible" content="IE=edge">
  7   <meta name="viewport" content="width=device-width, initial-scale=1.0">
  8   <title>getBoundingClientRect可视区域</title>
  9   <style>
 10     * {
 11       margin: 0
 12     }
 13 
 14     body {
 15       height: 2000px;
 16       width: 3000px;
 17     }
 18 
 19     .circle-wrap {
 20       position: fixed;
 21       top: 50px;
 22       right: 50px;
 23       padding: 10px;
 24       box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
 25       width: 140px;
 26       background: #fff;
 27     }
 28 
 29     .circle-wrap .circle {
 30       display: inline-block;
 31       width: 20px;
 32       height: 20px;
 33       border-radius: 50%;
 34       background: red;
 35     }
 36 
 37     .card-wrap {
 38       height: 400px;
 39       width: 600px;
 40       margin-top: 100px;
 41       margin-left: 100px;
 42       border: 1px solid green;
 43       overflow: auto;
 44     }
 45 
 46     .card-wrap .card {
 47       width: 200px;
 48       height: 200px;
 49       padding: 20px;
 50       box-sizing: border-box;
 51       box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
 52       margin-top: 500px;
 53       margin-left: 500px;
 54     }
 55     .head{
 56       width: 100%;
 57       height: 100px;
 58       background: pink;
 59       /* position: fixed; */
 60       top:0;
 61       left: 0;
 62     }
 63   </style>
 64 </head>
 65 
 66 <body>
 67   <!-- <div class="head"></div> -->
 68   <div class="circle-wrap">
 69     <span>当下方的卡片在可视区域内,我的圈圈是绿色,否则是红色</span>
 70     <span class="circle"></span>
 71   </div>
 72   <div class="card-wrap">
 73     <div class="card">我是一个卡片</div>
 74   </div>
 75   <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.js"></script>
 76   <script>
 77     const card = document.querySelector(".card");
 78     const circle = document.querySelector(".circle");
 79     const observer = new IntersectionObserver((changes) => {
 80       // changes是数组
 81       changes.forEach(one => {
 82         console.log(one)
 83         let isIntersecting = one.isIntersecting
 84         let background = null
 85         if (isIntersecting) {
 86           background = "green"
 87         } else {
 88           background = "red"
 89         }
 90         circle.style.background = background
 91       })
 92     })
 93     observer.observe(card)
 94     // console.log(observer)
 95     /* 取消观察特定的元素:observer.unobserve(dom) */
 96     /* 对象停止监听目标 observer.disconnect() */
 97   </script>
 98 </body>
 99 
100 </html>
IntersectionObserver可以传root,进一步判断dom是相对于哪个节点,来判断是否在可视区域内,默认root是document。
const observer = new IntersectionObserver((changes) => {
      console.log(changes)
      changes.forEach(one => {
        console.log(one)
        let isIntersecting = one.isIntersecting
        let background = null
        if (isIntersecting) {
          background = "green"
        } else {
          background = "red"
        }
        circle.style.background = background
      })
    },{root:document.querySelector(".card-wrap")})

 

 

 

 

posted @ 2023-02-10 16:01  蛙仔  阅读(919)  评论(0编辑  收藏  举报