基本用法
构造函数
new IntersectionObserver(callback, options);
callback 发生交叉的回调,接受一个entries参数,返回当前已监听并且发生了交叉的目标集合
new IntersectionObserver(entries => {
entries.forEach(item => console.log(item));
// ...
});
item 包含的常用属性
属性 | 说明 |
---|---|
boundingClientRect | 空间信息 |
intersectionRatio | 元素可见区域的占比 |
isIntersecting | 字面理解为是否正在交叉 ,可用做判断元素是否可见 |
target | 目标节点,就跟event.target 一样 |
注意:页面初始化的时候会触发一次callback
,entries
为所有已监听的目标集合
options 它是一个配置参数,对象类型,非必填,常用属性如下:
属性 | 类型 | 说明 |
---|---|---|
root | Object | 指定父元素,默认为视窗 |
rootMargin | 触发交叉 的偏移值,默认为”0px 0px 0px 0px”(上左下右,正数为向外扩散,负数则向内收缩) |
如果设置rootMargin为”20px 0px 30px 30px”,那么元素未到达视窗时,就已经切换为可见状态了:
常用方法
名称 | 说明 | 参数 |
---|---|---|
observe | 开始监听一个目标元素 | 节点 |
unobserve | 停止监听一个目标元素 | 节点 |
takeRecords | 返回所有监听的目标元素集合 | |
disconnect | 停止所有监听 |
简单例子
- 假设页面上有一个
class="box"
的盒子且父元素为视窗
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
position: relative;
top: 1000px;
background: lightgoldenrodyellow
}
</style>
</head>
<body>
<div class="box"></div>
<script>
let box = document.querySelector(".box");
let observer = new IntersectionObserver(entries => {
entries.forEach(item => {
let tips = item.isIntersecting ? "进入了父元素的内部" : "离开了父元素的内部";
console.log(tips);
});
});
observer.observe(box); // 监听一个box
</script>
</body>
</html>
当这个元素离开进入视窗后就会改变item.isIntersecting属性触发事件。
- 指定父元素
let child = document.querySelector(".child");
let observer = new IntersectionObserver(entries => {
entries.forEach(item => {
console.log(item.isIntersecting ? "可见" : "不可见");
});
}, {
root: document.querySelector(".parent")
});
observer.observe(child); // 开始监听child
传入IntersectionObserver
第二个参数root对象可以指定父元素
实际应用
图片懒加载
主要还是滑动到视窗内部触发改变isIntersecting
的事件,再把data-set里边的数据填充到src中去。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.lazyload {
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div style="height: 1000px"></div>
<div><img class="lazyload" data-origin="./test.png" src="" /></div>
<div><img class="lazyload" data-origin="./test.png" src="" /></div>
<div><img class="lazyload" data-origin="./test.png" src="" /></div>
<script>
let images = document.querySelectorAll("img.lazyload");
let observer = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
setTimeout(() => {
item.target.src = item.target.dataset.origin; // 开始加载图片
}, 1000);
observer.unobserve(item.target); // 停止监听已开始加载的图片
}
});
});
// 循环遍历每一个元素添加观测
images.forEach(item => observer.observe(item));
</script>
</body>
</html>
同样的通过监测视窗位的位置也能实现懒加载
https://blog.csdn.net/weixin_42519137/article/details/98672339
触底
触发触底事件,可进行数据的加载
new IntersectionObserver(entries => {
let item = entries[0]; // 拿第一个就行,反正只有一个
if (item.isIntersecting) console.log("滚动到了底部,开始请求数据");
}).observe(document.querySelector(".reference")); // 监听参照元素
在最底部增加一个元素,观测这个元素出现则继续加载。
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.lazyload {
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div style="height: 1000px"></div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
voluptatem. </div>
<div class="reference">lllll</div>
<script>
new IntersectionObserver(entries => {
let item = entries[0]; // 拿第一个就行,反正只有一个
if (item.isIntersecting) console.log("滚动到了底部,开始请求数据");
}).observe(document.querySelector(".reference")); // 监听参照元素
</script>
</body>
</html>
吸顶
吸顶可以通过css的粘贴属性position: sticky
实现,但是兼容性较差;
如果用交叉观察者
实现也很方便,同样也要放一个参照元素
;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.fixed {
position: fixed;
top: 0;
left: 0;
width: 100%;
}
</style>
</head>
<body>
<div style="height: 1000px"></div>
<div class="reference"></div>
<nav>我可以吸顶</nav>
<div style="height: 1000px"></div>
<script>
let nav = document.querySelector("nav");
let reference = document.querySelector(".reference");
new IntersectionObserver(entries => {
let item = entries[0];
let top = item.boundingClientRect.top;
/**
* 问题很明显,当给nav增加fixed定位时,nav脱离了文档流,
* 自然参考元素会往下掉,然后往下掉又发生了交叉,从而去除fixed定位,陷入一个死循环;
*/
reference.style.top = nav.offsetTop + "px";
/**
* 当参照元素的的top值小于0,也就是在视窗的顶部的时候,
* 开始吸顶,否则移除吸顶
*/
if (top < 0) nav.classList.add("fixed");
else nav.classList.remove("fixed");
}).observe(reference);
</script>
</body>
</html>
注意:这个吸顶在慢慢滑动的时候不太好使
动画展示
某个元素出现的时候就给该元素加个动画,比如渐变、偏移等;
demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
ul {
display: grid;
grid-template-columns: 50% 50%;
align-items: center;
justify-items: center;
}
li {
margin: 20px;
width: 300px;
height: 200px;
background: lightblue;
list-style: none;
}
li.show {
animation: left 2s ease;
}
li:nth-child(2n).show {
animation: right 2s ease;
}
li:nth-child(2n) {
background: lightcoral;
}
@keyframes left {
from {
opacity: 0;
transform: translate(-20px, 20px);
}
to {
opacity: 1;
}
}
@keyframes right {
from {
opacity: 0;
transform: translate(20px, 20px);
}
to {
opacity: 1;
}
}
</style>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
let list = document.querySelectorAll("ul li");
let observer = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
item.target.classList.add("show"); // 增加show类名
observer.unobserve(item.target); // 移除监听
}
});
});
list.forEach(item => observer.observe(item));
</script>
</body>
</html>