IntersectionObserver

昨天奇舞周刊推了一个“五分钟 get 你也许不知道的前端新特性”,里面有个关于lazy-load的东西:IntersectionObserver交叉观察器。
原文戳我!~

在追踪DOM元素进入可视区的时候,你可能想要在刚好的时间对图片进行懒加载,因为你需要知道用户是真的看到了某个确定的广告位。介个时候吧,可以监听scroll事件或者用一个周期性定时器在这个元素上调用getBoundingClientRect()。但是!这种方法实在忒慢了,因为每次调用getBoundingClientRect()的时候,浏览器会被强制对整个页面进行重排、并且产生大量的垃圾。当你知道你的网站被嵌入一个iframe而你想知道用户什么时候能看到某个元素的时候,这件事就有点儿坑爹了:单源模型和浏览器不会让你接近任何嵌在iframe当中的数据的。这对于被频繁嵌在iframe中加载的东西(拿广告来说)是个共性问题。

设计IntersectionObserver是为了让这个可见度测试更加有效,并且chrome51+已经支持了。IntersectionObserver会让你知道一个被观察的元素什么时候进入或离开可视区。

如何创建一个IntersectionObserver

它的API很小,举个栗子??就知道了:

var io = new IntersectionObserver(
    entries => {
        console.log(entries);
    },
    {
        /* Using default options. Details below */
    }
);
// Start observing an element
io.observe(element);

// Stop observing an element
// io.unobserve(element);

// Disable entire IntersectionObserver
// io.disconnect();

默认情况下,元素在出现和离开视口区的时候都会调用callback。
如果需要观察多个元素时,使用一个IntersectionObserver接口并多次调用observe(),这种方式是爸爸们比较欣慰的。

entries参数是向callback当中传入的一组IntersectionObserverEntry对象数组,每个IntersectionObserverEntry对象包含着相应被观察元素的信息,叫作“更新交叉点数据(什么鬼)”(看下面结构就知道了)。

??[IntersectionObserverEntry]
    time: 3893.92
  ??rootBounds: ClientRect
      bottom: 920
      height: 1024
      left: 0
      right: 1024
      top: 0
      width: 920
  ??boundingClientRect: ClientRect
    // ...
  ??intersectionRect: ClientRect
    // ...
    intersectionRatio: 0.54
  ??target: div#observee
    // ...

rootBounds是在根元素(默认就是viewport)矩形区域的信息,调用getBoundingClientRect()的返回值;
boundingClientRect是在目标元素(默认就是viewport),矩形区域的信息,调用getBoundingClientRect()的返回值;
intersectionRect是目标元素与根元素交叉区域的信息,并且能清楚地告诉你目标元素的哪个部分是可见的。
intersectionRatio目标元素的可见比例,密切相关的一个东西,它能告诉你元素当中有多大一部分是可见的(下图)。有了这个信息,你可以有效地实现一些功能,比如当资源在屏幕上可见之前刚好加载出来。


IntersectionObservers是异步传递数据的,同时callback会运行在主线程当中。此外,规范当中说了,IntersectionObservers的实现应该使用requestIdleCallback()。这就意味着你所提供的callback的调用是低优先级的,会在空闲时间进行。这是一个有意识的设计决定。

滚动的div

我自己并不热爱在一个元素内进行滚动,但在此先不做评论,对IntersectionObservers也是。
options对象采用根选项,可以让你去定义一个viewport的替代品作为你的根元素。记住这一点很重要:根元素是所有观察元素的祖先!

交叉所有的元素

永远不要酱紫!对于你用户的CPU周期来说这是不过脑子的用法。想象一个无穷无尽的滚动条:在这个剧情当中,向DOM当中添加哨兵并且(循环地)观察他们是明智的。你需要给滚动条当中的最后一个节点添加一个哨兵。当那个哨兵出现在视窗当中,你可以用callback去加载数据、创建下个元素、加在DOM上,并相应地重新摆放哨兵的位置。如果你适当地重新使用了哨兵,就不需要额外调用observe()了,现有的IntersectionObservers会持续运行下去。

更多更新(???)

之前提到过,当目标元素部分进入viewport或是它在其他时间离开viewport时,callback会被触发一次。在这种方式下IntersectionObservers会回答你这样的问题“元素X在视窗区么?”,但有时候酱紫是不够的。
下面threshold就要登场了:这个东西允许你定义一组intersectionRatio门槛,你的callback会在每次intersectionRatio越过其中一个值的时候被调用一次。它的默认值是[0],如果我们设置这样一组值:[0, 0.25, 0.5, 0.75, 1],我们会在元素的每四分之一出现的时候被告之。(拜说了看下图吧)

还有其他选项么?

到现在为止,上面列出的选项里面只剩一个了:rootMargin,允许你为根元素指明margin,有效地允许你增大或缩小交叉区域。这些margin跟CSS写法一样:"10px 20px 30px 40px"相对应的是上、右、下、左的margin值。总的来说,IntersectionObservers的选项结构是提供如下选项的:

new IntersectionObserver(entries => {/* … */}, {
  // The root to use for intersection.
  // If not provided, use the top-level document’s viewport.
  root = null,
  // Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.  
  // If an explicit root element is specified, components may be percentages of the
  // root element size.  If no explicit root element is specified, using a percentage
  // is an error.
  rootMargin = "0px",
  // Threshold(s) at which to trigger callback, specified as a ratio, or list of
  // ratios, of (visible area / total area) of the observed element (hence all
  // entries must be in the range [0, 1]).  Callback will be invoked when the visible
  // ratio of the observed element crosses a threshold in the list.
  threshold = [0],
});

iframe魔法(哈?)

IntersectionObservers专门为广告服务和社交网络设计了小组件,它们经常使用iframe,而且知道它们是否出现在视窗中是有好处的。如果一个iframe观察着它的一个元素,不管是滚动iframe还是滚动包含着iframe的window,都会在合适的时间触发callback。然而对于后一种情况,rootBounds将被置为null以避免跨越原始值泄露了数据。

有什么是IntersectionObservers不是的

有些事儿你得知道,IntersectionObservers有意地既不是像素完美也不是低延迟,用它来尝试实现像滚动相关的动画的话基本就是废物一个,严格讲,数据在你用它的时候是过期的。explainer 有关于原始用例的更多细节。

callback当中能做多少事儿?

在callback当中花费太多时间的话会让你的app滞后,所有常见做法都适用。

向前走,和你的元素相交(什么鬼)

浏览器对IntersectionObservers的支持还挺弱的,所以目前来看这东西不会在所有地方都好好生效的。与此同时,一个polyfill正在WICG的存储库中生效。显然,你用这个polyfill不会获得像native实现能带给你的性能优化。
你现在就能在Chrome Canary使用IntersectionObservers啦~ 不管你遇到什么,告诉我们。


奇舞周刊推的东西还是挺有营养的。。。
突然想起几天前被三百六的一哥们儿在一面的时候整整面了俩小时!两个小时?。?!那个一楼的面试间,贼冷好么!就当我觉得这哥哥肯定是说了算的吧?面俩小时是不是意味着直接HR了?偷笑ing的时候,人家淡定得说了说“先吃午饭去吧,下午接着二面”。( ⊙ o ⊙ )当时我就凌乱了,走出楼的时候连灵魂都在大风中骄傲放纵着。。。
三百六啊三百六,你是觉得你虐我千百遍,我还能待你如初恋么??~

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

推荐阅读更多精彩内容