IntersectionObserver API 手把手教学

IntersectionObserver API 是现代浏览器提供的一种异步观察目标元素与祖先元素(或顶级文档视窗)交叉状态的方法。它可以用于实现懒加载图片、无限滚动、以及其他需要监听元素可见性的功能。本文将详细介绍 IntersectionObserver API 的使用方法,帮助你快速掌握这一实用工具。

基本概念

在开始使用 IntersectionObserver 之前,我们需要了解几个基本概念:

  • 目标元素(Target Element):你想要观察的元素。
  • 根元素(Root Element):用来检测目标元素的可见性变化的容器元素,默认为浏览器视窗。
  • 阈值(Threshold):触发回调函数的目标元素可见比例。

为什么使用 IntersectionObserver

传统上,我们会使用 scroll 事件监听元素的可见性变化。然而,这种方法有几个缺点:

  • 性能问题:滚动事件会频繁触发,从而导致性能问题。
  • 复杂的计算:需要手动计算元素的可见性。
  • IntersectionObserver 可以解决这些问题:

性能更好:IntersectionObserver 是异步的,不会频繁触发。
简单易用:只需要定义一次观察逻辑,浏览器会处理所有计算。

创建 IntersectionObserver 实例

IntersectionObserver 是一个构造函数,我们需要先创建一个实例。构造函数接收两个参数:回调函数和可选配置对象。

let options = {
  root: null, // 默认为视窗
  rootMargin: '0px', // 视窗的外边距
  threshold: 0.1 // 目标元素可见比例达到 10% 时触发回调
};

let observer = new IntersectionObserver(callback, options);

定义回调函数

回调函数在目标元素的可见性变化时被调用。它接收两个参数:entries 和 observer。

let callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('目标元素进入视窗');
      // 在此处理进入视窗后的逻辑
    } else {
      console.log('目标元素离开视窗');
      // 在此处理离开视窗后的逻辑
    }
  });
};

观察目标元素

创建 IntersectionObserver 实例后,使用 observe 方法开始观察目标元素。

let target = document.querySelector('.target-element');
observer.observe(target);

停止观察

如果不再需要观察某个元素,可以使用 unobserve 方法。

observer.unobserve(target);

当不再需要 IntersectionObserver 实例时,可以调用 disconnect 方法停止观察所有目标元素。

observer.disconnect();

完整示例

以下是一个完整的示例,展示如何使用 IntersectionObserver API 实现懒加载图片。

  • HTML 部分:
<div class="container">
  <img data-src="image1.jpg" class="lazy-load" alt="Lazy Loaded Image 1">
  <img data-src="image2.jpg" class="lazy-load" alt="Lazy Loaded Image 2">
  <img data-src="image3.jpg" class="lazy-load" alt="Lazy Loaded Image 3">
</div>
  • CSS 部分:
.lazy-load {
  opacity: 0;
  transition: opacity 0.5s;
}
.lazy-load.loaded {
  opacity: 1;
}
  • JavaScript 部分:
document.addEventListener('DOMContentLoaded', () => {
  let lazyImages = document.querySelectorAll('.lazy-load');

  let lazyLoadCallback = (entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        let img = entry.target;
        img.src = img.dataset.src;
        img.onload = () => {
          img.classList.add('loaded');
        };
        observer.unobserve(img);
      }
    });
  };

  let observer = new IntersectionObserver(lazyLoadCallback, {
    root: null,
    rootMargin: '0px',
    threshold: 0.1
  });

  lazyImages.forEach(img => {
    observer.observe(img);
  });
});

更多示例和用法

示例一:无限滚动加载更多内容

  • HTML 部分:
<div class="content">
  <div class="item">内容1</div>
  <div class="item">内容2</div>
  <!-- 更多内容 -->
</div>
<div class="loading">加载中...</div>
  • JavaScript 部分:
document.addEventListener('DOMContentLoaded', () => {
  let loading = document.querySelector('.loading');

  let loadMoreContent = (entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        // 模拟加载更多内容
        for (let i = 0; i < 10; i++) {
          let newItem = document.createElement('div');
          newItem.className = 'item';
          newItem.textContent = `新内容${i + 1}`;
          document.querySelector('.content').appendChild(newItem);
        }
        observer.unobserve(entry.target); // 停止观察当前的 loading 元素
        observer.observe(document.querySelector('.loading')); // 继续观察新的 loading 元素
      }
    });
  };

  let observer = new IntersectionObserver(loadMoreContent, {
    root: null,
    rootMargin: '0px',
    threshold: 1.0 // 目标元素完全可见时触发回调
  });

  observer.observe(loading);
});

示例二:元素进入视窗时添加动画

  • HTML 部分:
<div class="container">
  <div class="box">Box 1</div>
  <div class="box">Box 2</div>
  <div class="box">Box 3</div>
</div>
  • CSS 部分:
.box {
  opacity: 0;
  transform: translateY(100px);
  transition: opacity 0.5s, transform 0.5s;
}
.box.visible {
  opacity: 1;
  transform: translateY(0);
}
  • JavaScript 部分:
document.addEventListener('DOMContentLoaded', () => {
  let boxes = document.querySelectorAll('.box');

  let animateBoxes = (entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('visible');
        observer.unobserve(entry.target); // 动画完成后停止观察
      }
    });
  };

  let observer = new IntersectionObserver(animateBoxes, {
    root: null,
    rootMargin: '0px',
    threshold: 0.1
  });

  boxes.forEach(box => {
    observer.observe(box);
  });
});

结论

IntersectionObserver API 是一个强大的工具,它能轻松实现懒加载、无限滚动,以及其他需要监听元素可见性的功能。通过本教程,你应该已经学会了如何创建 IntersectionObserver 实例、定义回调函数、观察和停止观察目标元素。如果你在项目中需要优化页面性能或实现动态内容加载,那么 IntersectionObserver 绝对是一个值得尝试的技术。

原文链接

IntersectionObserver API 手把手教学

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354