js 可视区域的判断
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")})