基本用法

构造函数

  1. new IntersectionObserver(callback, options);

callback 发生交叉的回调,接受一个entries参数,返回当前已监听并且发生了交叉的目标集合

  1. new IntersectionObserver(entries => {
  2. entries.forEach(item => console.log(item));
  3. // ...
  4. });

item 包含的常用属性

属性 说明
boundingClientRect 空间信息
intersectionRatio 元素可见区域的占比
isIntersecting 字面理解为是否正在交叉,可用做判断元素是否可见
target 目标节点,就跟event.target一样

注意:页面初始化的时候会触发一次callbackentries所有已监听的目标集合

options 它是一个配置参数,对象类型,非必填,常用属性如下:

属性 类型 说明
root Object 指定父元素,默认为视窗
rootMargin 触发交叉的偏移值,默认为”0px 0px 0px 0px”(上左下右,正数为向外扩散,负数则向内收缩)

如果设置rootMargin为”20px 0px 30px 30px”,那么元素未到达视窗时,就已经切换为可见状态了:

image.png

常用方法

名称 说明 参数
observe 开始监听一个目标元素 节点
unobserve 停止监听一个目标元素 节点
takeRecords 返回所有监听的目标元素集合
disconnect 停止所有监听

简单例子

  1. 假设页面上有一个class="box"的盒子且父元素为视窗
  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. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <style>
  9. .box {
  10. width: 200px;
  11. height: 200px;
  12. position: relative;
  13. top: 1000px;
  14. background: lightgoldenrodyellow
  15. }
  16. </style>
  17. </head>
  18. <body>
  19. <div class="box"></div>
  20. <script>
  21. let box = document.querySelector(".box");
  22. let observer = new IntersectionObserver(entries => {
  23. entries.forEach(item => {
  24. let tips = item.isIntersecting ? "进入了父元素的内部" : "离开了父元素的内部";
  25. console.log(tips);
  26. });
  27. });
  28. observer.observe(box); // 监听一个box
  29. </script>
  30. </body>
  31. </html>

当这个元素离开进入视窗后就会改变item.isIntersecting属性触发事件。

  1. 指定父元素
  1. let child = document.querySelector(".child");
  2. let observer = new IntersectionObserver(entries => {
  3. entries.forEach(item => {
  4. console.log(item.isIntersecting ? "可见" : "不可见");
  5. });
  6. }, {
  7. root: document.querySelector(".parent")
  8. });
  9. observer.observe(child); // 开始监听child

传入IntersectionObserver第二个参数root对象可以指定父元素


实际应用

图片懒加载

主要还是滑动到视窗内部触发改变isIntersecting的事件,再把data-set里边的数据填充到src中去。

  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. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <style>
  9. .lazyload {
  10. width: 200px;
  11. height: 200px;
  12. }
  13. </style>
  14. </head>
  15. <body>
  16. <div style="height: 1000px"></div>
  17. <div><img class="lazyload" data-origin="./test.png" src="" /></div>
  18. <div><img class="lazyload" data-origin="./test.png" src="" /></div>
  19. <div><img class="lazyload" data-origin="./test.png" src="" /></div>
  20. <script>
  21. let images = document.querySelectorAll("img.lazyload");
  22. let observer = new IntersectionObserver(entries => {
  23. entries.forEach(item => {
  24. if (item.isIntersecting) {
  25. setTimeout(() => {
  26. item.target.src = item.target.dataset.origin; // 开始加载图片
  27. }, 1000);
  28. observer.unobserve(item.target); // 停止监听已开始加载的图片
  29. }
  30. });
  31. });
  32. // 循环遍历每一个元素添加观测
  33. images.forEach(item => observer.observe(item));
  34. </script>
  35. </body>
  36. </html>

同样的通过监测视窗位的位置也能实现懒加载
https://blog.csdn.net/weixin_42519137/article/details/98672339

触底

触发触底事件,可进行数据的加载

  1. new IntersectionObserver(entries => {
  2. let item = entries[0]; // 拿第一个就行,反正只有一个
  3. if (item.isIntersecting) console.log("滚动到了底部,开始请求数据");
  4. }).observe(document.querySelector(".reference")); // 监听参照元素

在最底部增加一个元素,观测这个元素出现则继续加载。
完整代码:

  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. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <style>
  9. .lazyload {
  10. width: 200px;
  11. height: 200px;
  12. }
  13. </style>
  14. </head>
  15. <body>
  16. <div style="height: 1000px"></div>
  17. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  18. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  19. voluptatem. </div>
  20. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  21. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  22. voluptatem. </div>
  23. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  24. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  25. voluptatem. </div>
  26. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  27. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  28. voluptatem. </div>
  29. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  30. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  31. voluptatem. </div>
  32. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  33. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  34. voluptatem. </div>
  35. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  36. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  37. voluptatem. </div>
  38. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  39. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  40. voluptatem. </div>
  41. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  42. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  43. voluptatem. </div>
  44. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  45. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  46. voluptatem. </div>
  47. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  48. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  49. voluptatem. </div>
  50. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  51. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  52. voluptatem. </div>
  53. <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error aspernatur laudantium illo, porro non quod
  54. explicabo voluptatibus rem dolorum modi. Maiores qui nulla, deserunt inventore natus ab asperiores corporis
  55. voluptatem. </div>
  56. <div class="reference">lllll</div>
  57. <script>
  58. new IntersectionObserver(entries => {
  59. let item = entries[0]; // 拿第一个就行,反正只有一个
  60. if (item.isIntersecting) console.log("滚动到了底部,开始请求数据");
  61. }).observe(document.querySelector(".reference")); // 监听参照元素
  62. </script>
  63. </body>
  64. </html>

吸顶

吸顶可以通过css的粘贴属性position: sticky实现,但是兼容性较差;
如果用交叉观察者实现也很方便,同样也要放一个参照元素

  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. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <style>
  9. .fixed {
  10. position: fixed;
  11. top: 0;
  12. left: 0;
  13. width: 100%;
  14. }
  15. </style>
  16. </head>
  17. <body>
  18. <div style="height: 1000px"></div>
  19. <div class="reference"></div>
  20. <nav>我可以吸顶</nav>
  21. <div style="height: 1000px"></div>
  22. <script>
  23. let nav = document.querySelector("nav");
  24. let reference = document.querySelector(".reference");
  25. new IntersectionObserver(entries => {
  26. let item = entries[0];
  27. let top = item.boundingClientRect.top;
  28. /**
  29. * 问题很明显,当给nav增加fixed定位时,nav脱离了文档流,
  30. * 自然参考元素会往下掉,然后往下掉又发生了交叉,从而去除fixed定位,陷入一个死循环;
  31. */
  32. reference.style.top = nav.offsetTop + "px";
  33. /**
  34. * 当参照元素的的top值小于0,也就是在视窗的顶部的时候,
  35. * 开始吸顶,否则移除吸顶
  36. */
  37. if (top < 0) nav.classList.add("fixed");
  38. else nav.classList.remove("fixed");
  39. }).observe(reference);
  40. </script>
  41. </body>
  42. </html>

注意:这个吸顶在慢慢滑动的时候不太好使

动画展示

某个元素出现的时候就给该元素加个动画,比如渐变、偏移等;

demo

  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. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. <style>
  9. ul {
  10. display: grid;
  11. grid-template-columns: 50% 50%;
  12. align-items: center;
  13. justify-items: center;
  14. }
  15. li {
  16. margin: 20px;
  17. width: 300px;
  18. height: 200px;
  19. background: lightblue;
  20. list-style: none;
  21. }
  22. li.show {
  23. animation: left 2s ease;
  24. }
  25. li:nth-child(2n).show {
  26. animation: right 2s ease;
  27. }
  28. li:nth-child(2n) {
  29. background: lightcoral;
  30. }
  31. @keyframes left {
  32. from {
  33. opacity: 0;
  34. transform: translate(-20px, 20px);
  35. }
  36. to {
  37. opacity: 1;
  38. }
  39. }
  40. @keyframes right {
  41. from {
  42. opacity: 0;
  43. transform: translate(20px, 20px);
  44. }
  45. to {
  46. opacity: 1;
  47. }
  48. }
  49. </style>
  50. </head>
  51. <body>
  52. <ul>
  53. <li></li>
  54. <li></li>
  55. <li></li>
  56. <li></li>
  57. <li></li>
  58. <li></li>
  59. <li></li>
  60. <li></li>
  61. <li></li>
  62. <li></li>
  63. <li></li>
  64. <li></li>
  65. <li></li>
  66. <li></li>
  67. <li></li>
  68. <li></li>
  69. <li></li>
  70. <li></li>
  71. <li></li>
  72. <li></li>
  73. <li></li>
  74. <li></li>
  75. <li></li>
  76. </ul>
  77. <script>
  78. let list = document.querySelectorAll("ul li");
  79. let observer = new IntersectionObserver(entries => {
  80. entries.forEach(item => {
  81. if (item.isIntersecting) {
  82. item.target.classList.add("show"); // 增加show类名
  83. observer.unobserve(item.target); // 移除监听
  84. }
  85. });
  86. });
  87. list.forEach(item => observer.observe(item));
  88. </script>
  89. </body>
  90. </html>