大家下午好。听说在实现一些需要监听滚动条滚动到某个元素需要执行某段神秘代码的需求时,大伙总是不太乐意,现给大家介绍一个不用监听滚动事件就能完成需求的api,希望能给一点点启发。

废话少说,先上效果

  • 懒加载

  • 瀑布流

  • 视频暂停

上代码

HTML

 1/* html */
 2<!DOCTYPE html>
 3<html lang="en">
 4<head>
 5  <meta charset="UTF-8">
 6  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 7  <title>Intersection Observer API</title>
 8  <style>
 9    * {
10      padding: 0;
11      margin: 0;
12    }
13    .wrapper {
14      display: flex;
15      flex-wrap: wrap;
16    }
17    img {
18      width: 400px;
19      margin: auto;
20    }
21  </style>
22</head>
23<body>
24  <div class="wrapper">
25    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF">
26    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF">
27    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1223089837,2011531023&fm=193&f=GIF">
28    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF">
29    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=727460147,2222092211&fm=193&f=GIF">
30    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF">
31    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2763645735,2016465681&fm=193&f=GIF">
32    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=38705621,4040292245&fm=193&f=GIF">
33    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3691080281,11347921&fm=193&f=GIF">
34    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=309902808,4013662088&fm=193&f=GIF">
35    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3915743384,2060495762&fm=193&f=GIF">
36    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=814015239,3764270914&fm=193&f=GIF">
37    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=735474575,14049849&fm=193&f=GIF">
38    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=55748064,2074988836&fm=193&f=GIF">
39    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1415984692,3889465312&fm=193&f=GIF">
40    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1635608122,693552335&fm=193&f=GIF">
41    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=4229354459,3466205664&fm=193&f=GIF">
42    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF">
43    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF">
44    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=801209673,1770377204&fm=193&f=GIF">
45    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1010739515,2488150950&fm=193&f=GIF">
46    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=755629088,1584786319&fm=193&f=GIF">
47    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2359570649,2574326109&fm=193&f=GIF">
48    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3326728979,3352004316&fm=193&f=GIF">
49    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1972611367,1123866192&fm=193&f=GIF">
50    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1418407542,1794380926&fm=193&f=GIF">
51    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1879685954,2134278564&fm=193&f=GIF">
52    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2466425392,342874463&fm=193&f=GIF">
53    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3355440349,3059059541&fm=193&f=GIF">
54    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1335405753,180526972&fm=193&f=GIF">
55    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3522949495,3570538969&fm=193&f=GIF">
56    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=434014116,2108959724&fm=193&f=GIF">
57    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3439093793,987421329&fm=193&f=GIF">
58    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=238793329,1979556309&fm=193&f=GIF">
59    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3355871248,953827123&fm=193&f=GIF">
60    <img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2757385166,2243114851&fm=193&f=GIF">
61  </div>
62  <script src="./index.js"></script>
63</body>
64</html>

JS

 1
 2const ob = new IntersectionObserver(entries => {
 3  entries.forEach(entrie => {
 4    if(entrie.isIntersecting) {
 5        const img = entrie.target;
 6        img.src = img.dataset.src;
 7        ob.unobserve(img)
 8    }
 9  })
10}, {
11  threshold: 0
12})
13
14const imgs = document.querySelectorAll('img[data-src]');
15imgs.forEach(img => {
16  ob.observe(img)
17})

完事,下次再见

啊?客官还要解释?那好吧

接下来就是隆重推出今天的主角 IntersectionObserver API,这是一套用来监听 html 元素是否与指定元素有 交叉关系 的API。 就拿以下代码解释

 1const ob = new IntersectionObserver(
 2  (entries) => {
 3    console.log(entries);
 4  },
 5  {
 6    root: null,
 7    rootMargin: 0,
 8    threshold: 0,
 9  }
10);
11ob.observe(dom);
  • IntersectionObserver 第一个参数是回调函数,回调参数是一组被观察元素的具体交叉情况

  • 第二个参数是配置项

    • root: 表示被观察元素与哪个元素是否产生交叉,只能填被观察元素的父元素(祖先元素),默认 null 表示视口元素
    • rootMargin: 表示被观察元素与 root 之间的距离,如值为 10 ,则被观察元素与 root 相距小于等于 10 就会视为已交叉;默认为0;如下图

    • threshold: 表示交叉的阈值,值为 0-1 之间,默认为0,表示交叉了多少才视为交叉,如下图

  • ob.observe(dom) 表示要观察哪个元素;
  • 下图为回调函数中的参数;

主要关注 isInterestingtarget 这两个属性

  • isInteresting 表示是否交叉
  • target 表示目前交叉的元素

回归主题

那么我们回到上面的懒加载的实现,只要监听每张图片是否与视口交叉即可。请看

 1const ob = new IntersectionObserver(entries => {
 2  entries.forEach(entrie => {
 3    if(entrie.isIntersecting) {
 4        const img = entrie.target; 
 5        img.src = img.dataset.src; 
 6        ob.unobserve(img) 
 7    }
 8  })
 9}, {
10  threshold: 0 
11})
12
13const imgs = document.querySelectorAll('img[data-src]'); 
14imgs.forEach(img => {
15  ob.observe(img) 
16})

实现瀑布流,只需监听加载中的元素是否交叉即可

HTML

 1<!DOCTYPE html>
 2<html lang="en">
 3  <head>
 4    <meta charset="UTF-8" />
 5    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6    <title>Intersection Observer API</title>
 7    <style>
 8      * {
 9        padding: 0;
10        margin: 0;
11      }
12      .wrapper {
13        display: flex;
14        flex-wrap: wrap;
15      }
16      img {
17        width: 350px;
18        margin: auto;
19      }
20      .mycircle {
21        stroke-dasharray: 125.6px;
22        animation: move linear 1s infinite;
23        transform-origin: center;
24        stroke-dashoffset: 30px;
25        fill: none;
26        stroke: #666;
27        stroke-width: 5px;
28      }
29      @keyframes move {
30        0% {
31          transform: rotate(0deg);
32        }
33        100% {
34          transform: rotate(360deg);
35        }
36      }
37    </style>
38  </head>
39  <body>
40    <div class="wrapper">
41      <img src="https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF">
42      <img src="https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF">
43      <img src="https://t7.baidu.com/it/u=1223089837,2011531023&fm=193&f=GIF">
44      <img src="https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF">
45      <img src="https://t7.baidu.com/it/u=727460147,2222092211&fm=193&f=GIF">
46      <img src="https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF">
47      <img src="https://t7.baidu.com/it/u=2763645735,2016465681&fm=193&f=GIF">
48      <img src="https://t7.baidu.com/it/u=38705621,4040292245&fm=193&f=GIF">
49      <img src="https://t7.baidu.com/it/u=3691080281,11347921&fm=193&f=GIF">
50      <img src="https://t7.baidu.com/it/u=309902808,4013662088&fm=193&f=GIF">
51      <img src="https://t7.baidu.com/it/u=3915743384,2060495762&fm=193&f=GIF">
52    </div>
53    <div class="spin" style="text-align: center">
54      <svg width="150" height="150" viewBox="0 0 50 50">
55        <circle class="mycircle" cx="25" cy="25" r="20">
56          <animateTransform
57            attributeName="transform"
58            type="rotate"
59            from="0"
60            to="360"
61            dur="1s"
62            repeatCount="indefinite"
63          />
64        </circle>
65      </svg>
66    </div>
67    <script src="./index2.js"></script>
68  </body>
69</html>

JS

 1const img = [
 2    "https://t7.baidu.com/it/u=814015239,3764270914&fm=193&f=GIF",
 3    "https://t7.baidu.com/it/u=735474575,14049849&fm=193&f=GIF",
 4    "https://t7.baidu.com/it/u=55748064,2074988836&fm=193&f=GIF",
 5    "https://t7.baidu.com/it/u=1415984692,3889465312&fm=193&f=GIF",
 6    "https://t7.baidu.com/it/u=1635608122,693552335&fm=193&f=GIF",
 7    "https://t7.baidu.com/it/u=4229354459,3466205664&fm=193&f=GIF",
 8    "https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF",
 9    "https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF",
10    "https://t7.baidu.com/it/u=801209673,1770377204&fm=193&f=GIF",
11    "https://t7.baidu.com/it/u=1010739515,2488150950&fm=193&f=GIF",
12    "https://t7.baidu.com/it/u=755629088,1584786319&fm=193&f=GIF",
13    "https://t7.baidu.com/it/u=2359570649,2574326109&fm=193&f=GIF",
14    "https://t7.baidu.com/it/u=3326728979,3352004316&fm=193&f=GIF",
15  ]
16function loadImage(imgs) {
17  imgs.forEach(item => {
18    const img = document.createElement('img')
19    img.src = item
20    document.querySelector('.wrapper').appendChild(img)
21  })
22}
23const ob = new IntersectionObserver(entries => {
24  if(entries[0].isIntersecting) {
25    setTimeout(() => {
26      loadImage(img)
27    }, 1000);
28  }
29}, {
30  threshold: 0
31})
32const spin = document.querySelector('.spin');
33ob.observe(spin) 

实现视频暂停

HTML

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4  <meta charset="UTF-8">
 5  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6  <title>Document</title>
 7  <style>
 8    * {
 9      margin: 0;
10      padding: 0;
11    }
12    .wrapper {
13      width: 100vw;
14      height: 100vh;
15      text-align: center;
16    }
17    .video {
18      margin-top: 100px;
19      margin-bottom: 1000px;
20    }
21  </style>
22</head>
23<body>
24  <div class="wrapper">
25    <video class="video" controls autoplay="true" src="哪吒之魔童降世.Nezha.Birth.of.the.Demon.Child.2019.HD1080P.国语中字.mp4" width="80%"></video>
26  </div>
27  <script src="./index3.js"></script>
28</body>
29</html>

JS

 1const ob = new IntersectionObserver(entries => {
 2    const video = entries[0].target
 3    if(entries[0].isIntersecting) {
 4    video.play()
 5  } else {
 6    video.pause()
 7  }
 8}, {
 9  threshold: 1 
10})
11
12const video = document.querySelector('.video');
13ob.observe(video)

真的没了,下次再见!

个人笔记记录 2021 ~ 2025