Method 1: getBoundingClientRect
usage
let domRect = dom.getBoundingClientRect();
DOMRect:{
x/left:视图原点(左上角)距离dom左边框距离,
y/top:视图原点(左上角)距离dom上边框距离,
right:视图原点(左上角)距离dom右边框距离,
bottom:视图原点(左上角)距离dom底边框距离,
width:dom的宽度,标准盒模型,width = 宽度+padding+border;怪异盒模型,width = 设置的宽度,
height:dom的高度,
}
So we can judge whether is within the visible area based on each attribute in DOMRect
. dom
eg:
<!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>getBoundingClientRect可视区域</title>
<style>
* {
margin: 0
}
.circle-wrap {
position: fixed;
top: 50px;
right: 50px;
padding: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: 140px;
background: #fff;
}
.circle-wrap .circle {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
background: red;
}
.card-wrap {
height: 2000px;
width: 3000px;
margin-top: 100px;
}
.card-wrap .card {
width: 200px;
height: 200px;
padding: 20px;
box-sizing: border-box;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-top: 500px;
margin-left: 500px;
}
</style>
</head>
<body>
<div class="circle-wrap">
<span>当下方的卡片在可视区域内,我的圈圈是绿色,否则是红色</span>
<span class="circle"></span>
</div>
<div class="card-wrap">
<div class="card">我是一个卡片</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.js"></script>
<script>
const card = document.querySelector(".card");
const circle = document.querySelector(".circle");
const getBoundingClientRectJudge = () => {
let domRect = card.getBoundingClientRect();
console.log(domRect)
let ch = document.documentElement.clientHeight;
let cw = document.documentElement.clientWidth;
let isInsert = true;
if (domRect.bottom < 0 || domRect.top > ch || domRect.right < 0 || domRect.left > cw) {
isInsert = false;
}
let background = null
if (isInsert) {
background = "green"
} else {
background = "red"
}
circle.style.background = background
}
window.addEventListener("scroll", _.throttle(getBoundingClientRectJudge, 500))
getBoundingClientRectJudge()
</script>
</body>
</html>
Rendering:
question
getBoundingClientRect
cannot satisfy all situations. It can even be said that it can only satisfy the judgment of one situation, that is the node that needs to be judged. dom
is only in the situation of a scroll bar. Down. What does it mean? All its ancestor nodes have only one scroll bar in total. If its parent node has a scroll bar and its parent node also has a scroll bar, then this method will not work. Please see the example: < /span>
The result shown below is wrong, the circle should be red.
<!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>getBoundingClientRect可视区域</title>
<style>
* {
margin: 0
}
body {
height: 2000px;
width: 3000px;
}
.circle-wrap {
position: fixed;
top: 50px;
right: 50px;
padding: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: 140px;
background: #fff;
}
.circle-wrap .circle {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
background: red;
}
.card-wrap {
height: 400px;
width: 600px;
margin-top: 100px;
margin-left: 100px;
border: 1px solid green;
overflow: auto;
}
.card-wrap .card {
width: 200px;
height: 200px;
padding: 20px;
box-sizing: border-box;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-top: 500px;
margin-left: 500px;
}
.head {
width: 100%;
height: 100px;
background: pink;
/* position: fixed; */
top: 0;
left: 0;
}
</style>
</head>
<body>
<!-- <div class="head"></div> -->
<div class="circle-wrap">
<span>当下方的卡片在可视区域内,我的圈圈是绿色,否则是红色</span>
<span class="circle"></span>
</div>
<div class="card-wrap">
<div class="card">我是一个卡片</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.js"></script>
<script>
const card = document.querySelector(".card");
const circle = document.querySelector(".circle");
const getBoundingClientRectJudge = () => {
let domRect = card.getBoundingClientRect();
let ch = document.documentElement.clientHeight;
let cw = document.documentElement.clientWidth;
let isInsert = true;
if (domRect.bottom < 0 || domRect.top > ch || domRect.right < 0 || domRect.left > cw) {
isInsert = false;
}
let background = null
if (isInsert) {
background = "green"
} else {
background = "red"
}
circle.style.background = background
}
window.addEventListener("scroll", _.throttle(getBoundingClientRectJudge, 500))
getBoundingClientRectJudge()
</script>
</body>
</html>
From this, we introduce the second method, IntersectionObserver.
Method 2: IntersectionObserver
usage
eg:
<!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>getBoundingClientRect可视区域</title>
<style>
* {
margin: 0
}
body {
height: 2000px;
width: 3000px;
}
.circle-wrap {
position: fixed;
top: 50px;
right: 50px;
padding: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: 140px;
background: #fff;
}
.circle-wrap .circle {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
background: red;
}
.card-wrap {
height: 400px;
width: 600px;
margin-top: 100px;
margin-left: 100px;
border: 1px solid green;
overflow: auto;
}
.card-wrap .card {
width: 200px;
height: 200px;
padding: 20px;
box-sizing: border-box;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
margin-top: 500px;
margin-left: 500px;
}
.head{
width: 100%;
height: 100px;
background: pink;
/* position: fixed; */
top:0;
left: 0;
}
</style>
</head>
<body>
<!-- <div class="head"></div> -->
<div class="circle-wrap">
<span>当下方的卡片在可视区域内,我的圈圈是绿色,否则是红色</span>
<span class="circle"></span>
</div>
<div class="card-wrap">
<div class="card">我是一个卡片</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.js"></script>
<script>
const card = document.querySelector(".card");
const circle = document.querySelector(".circle");
const observer = new IntersectionObserver((changes) => {
// 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
})
})
observer.observe(card)
// console.log(observer)
/* 取消观察特定的元素:observer.unobserve(dom) */
/* 对象停止监听目标 observer.disconnect() */
</script>
</body>
</html>
Additional information
IntersectionObserver
can be passedroot
to further determine which node dom
is relative to to determine whether it is within the visible area. The default is root
Yesdocument
.
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")})
Method 3: offsetTop, scrollTop
illustrate
offsetTop
: The pixel distance between the top outer border of the element and the top inner border of the containing element, the other directions are the sameoffsetWidth
:The width of both ends of the element including the outer border, the other directions are the samescrollLeft
andscrollTop
: You can both determine the scrolling state of the current element and set the scrolling position of the elementscrollWidth
andscrollHeight
: Determine the actual size of the element contentclientWidth
:The width of the content area of the element plus the width of the left and right padding, that isclientWidth = content + padding
clientHeight
:The height of the content area of the element plus the height of the top and bottom padding, that isclientHeight = content + padding
use
official:
0 <= el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
Writing method:
function isInViePortOfOne(el){
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight||document.body.clientHeight
const offsetTop = el.offsetTop;
const scollTop = document.documentElement.scrollTop
const top = offsetTop - scollTop;
return top <= viewPortHeight && top >= 0
}
Project attachment:Click here to download