基础知识
Element.clientHeight & Element.clientWidth
clientWidth:元素内容区宽度加上左右内边距宽度,即clientWidth = content + paddingclientHeight:元素内容区高度加上上下内边距高度,即clientHeight = content + padding
offsetTop
offsetTop 是 JavaScript 中一个用于获取元素相对于其包含元素的顶部位置的属性。它返回元素的上边界到其包含元素(offset parent)的上边界的距离,以像素为单位。
注意事项
- 滚动:
offsetTop不会受到滚动的影响。如果包含元素或其祖先元素有滚动条,offsetTop返回的值仍然是元素相对于包含元素的初始位置。 - 包含元素:包含元素(offset parent)通常是离元素最近的已定位祖先元素。如果没有已定位祖先元素,则为
<body>元素。
scrollTop
scrollWidth和scrollHeight用于获取一个元素的完整内容的宽度和高度,包括由于溢出而不可见的部分。这两个属性通常用于带有滚动条的元素,来测量内容的总尺寸,当内容动态变化时,scrollWidth和scrollHeight也会相应地变化。scrollTop用来获取或设置元素的垂直滚动位置。它返回一个整数值,表示元素的内容顶部被卷起来的像素值。
getBoundingClientRect
3种方法
1. el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
使用 el.offsetTop - document.documentElement.scrollTop <= viewPortHeight 判断一个元素是否在可视区域中有几个潜在的问题:
- 只考虑了元素的顶部边界:该公式只考虑了元素的顶部边界是否在可视区域内,没有考虑元素的底部边界。这意味着如果元素非常高,只有一部分在可视区域内,该公式仍然会返回
true。 - 未考虑负值情况:当元素的顶部边界在视口上方时,该公式不会处理这种情况,因为
offsetTop - scrollTop可能会是负值。
为了更准确地判断一个元素是否在可视区域中,我们可以使用以下方法:
解决方案
我们可以使用以下方法来判断一个元素是否在可视区域中,包括考虑元素的顶部和底部边界:
-
计算元素的位置:
- 元素的顶部边界相对于视口的位置:
el.offsetTop - document.documentElement.scrollTop - 元素的底部边界相对于视口的位置:
el.offsetTop + el.offsetHeight - document.documentElement.scrollTop
- 元素的顶部边界相对于视口的位置:
-
判断顶部和底部边界是否在可视区域中:
- 元素的顶部边界在视口下方(大于等于 0)。
- 元素的底部边界在视口上方(小于等于视口高度)。
事例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>isInViewPort Example</title>
<style>
.container {
height: 1500px;
background: linear-gradient(to bottom, red, yellow, green, blue, purple);
}
.box {
position: absolute;
top: 800px;
width: 100px;
height: 100px;
background-color: black;
}
</style>
</head>
<body>
<div class="container">
<div class="box" id="box"></div>
</div>
<script>
function isInViewPort(el) {
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight;
const scrollTop = document.documentElement.scrollTop;
const elementTop = el.offsetTop - scrollTop;
const elementBottom = el.offsetTop + el.offsetHeight - scrollTop;
return elementTop < viewPortHeight && elementBottom > 0;
}
var box = document.getElementById('box');
window.addEventListener('scroll', function() {
if (isInViewPort(box)) {
console.log('Box is in the viewport');
} else {
console.log('Box is not in the viewport');
}
});
</script>
</body>
</html>
2. 利用getBoundingClientRect获取DOMRect对象
如果一个元素在视窗之内的话,那么它一定满足下面两个条件:
- 以
top为参照点:top >= 0 && top <= viewPortHeight- 以
bottom为参照点:bottom >= 0 && bottom <= viewPortHeight
demo
<div class="content"></div>
<div class="box" id="box"></div>
.content {
height: 2000px;
background: linear-gradient(to bottom, red, yellow, green, blue, purple);
}
.box {
width: 100px;
height: 100px;
background: black;
position: absolute;
top: 1500px;
}
window.addEventListener("scroll", function () {
const box = document.getElementById("box");
const inView = isInViewPortOfOne(box);
console.log("Is in viewport:", inView);
});
function isInViewPortOfOne(el) {
const viewPortHeight =
window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight;
const rect = el.getBoundingClientRect();
const top = rect.top;
const bottom = rect.bottom;
return (
(top >= 0 && top <= viewPortHeight) ||
(bottom >= 0 && bottom <= viewPortHeight)
);
}
3. Intersection Observer
参考:1. IntersectionObserver;2. IntersectionObserverEntry
Intersection Observer 即重叠观察者,从这个命名就可以看出它用于判断两个元素是否重叠,因为不用进行事件的监听,性能方面相比getBoundingClientRect会好很多
使用步骤主要分为两步:创建观察者和传入被观察者
// 创建 Intersection Observer 实例
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("Box is in the viewport!");
} else {
console.log("Box is out of the viewport!");
}
});
},
{
root: null, // 视口为根元素
rootMargin: "0px",
threshold: 0.1, // 元素进入视口的阈值
}
);
// 选择要观察的元素
const box = document.getElementById("box");
observer.observe(box);