方法1:getBoundingClientRect
用法
js
let domRect = dom.getBoundingClientRect();
js
DOMRect:{
x/left:视图原点(左上角)距离dom左边框距离,
y/top:视图原点(左上角)距离dom上边框距离,
right:视图原点(左上角)距离dom右边框距离,
bottom:视图原点(左上角)距离dom底边框距离,
width:dom的宽度,标准盒模型,width = 宽度+padding+border;怪异盒模型,width = 设置的宽度,
height:dom的高度,
}
所以我们可以根据DOMRect
的中的各个属性来判断dom
是否在可视区域内。
eg:
js
<!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>
效果图:
问题
getBoundingClientRect
并不能满足所有情况,甚至说,它只能满足一种情况的判断,那就是需要判断的那个dom
节点,它只身处在一个滚动条的情况下。什么意思呢,就是它所有的祖先节点,加起来的滚动条只有1个,如果它父节点有滚动条,父父节点也有滚动条,那这种方法就不好用了,请看示例:
下面展示的结果是错误的,圈圈应该是红色才对。
js
<!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>
由此,我们引出第二种方法,IntersectionObserver。
方法2:IntersectionObserver
用法
eg:
js
<!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>
补充说明
IntersectionObserver
可以传root
,进一步判断dom
是相对于哪个节点,来判断是否在可视区域内,默认root
是document
。
js
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")})
方法3:offsetTop、scrollTop
说明
offsetTop
:元素的上外边框至包含元素的上内边框之间的像素距离,其他方向相同offsetWidth
:元素两端算上外边框的宽度,其他方向相同scrollLeft
和scrollTop
:既可以确定当前元素的滚动状态,也可以设置元素的滚动位置scrollWidth
和scrollHeight
:确定元素内容的实际大小clientWidth
:元素内容区宽度加上左右内边距宽度,即clientWidth = content + padding
clientHeight
:元素内容区高度加上上下内边距高度,即clientHeight = content + padding
使用
公式:
js
0 <= el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
写法:
js
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
}