您好,登錄后才能下訂單哦!
這篇文章主要講解了“IntersectionObserver怎么使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“IntersectionObserver怎么使用”吧!
作為一款產品,往往希望能得到用戶的反饋,從而通過對用戶行為的分析進行功能、交互等方面的改進。然而直接的一對一的用戶交流是低效且困難的,因此最普遍的做法便是通過數據埋點來反推用戶的行為。那么數據埋點中很重要的一環便是:曝光。
所謂曝光,便是頁面被展示的時候進行打點。舉個簡單的例子:用戶進入分類頁面,商品以行為單位從上而下進行排列。當用戶滾動頁面時,之前不在視窗范圍內的商品就會出現,此時,這部分商品就算曝光了,需要進行一次記錄。
那么為了實現上面功能,最普遍的做法有兩個。其一:監聽滾動事件,然后計算某個商品與視窗的相對位置,從而判斷是否可見。其二:設置一個定時器,然后以固定的時間為間隔計算某個商品與視窗的相對位置。
上面兩種做法在某種程度上能夠實現我們的目的,但是會有一些問題,比如最明顯的:慢。因為計算相對位置時會調用getBoundingClientRect(),這個api會導致瀏覽器進行全頁面的重新布局,影響性能,特別是在頻繁進行時。因此IntersectionObserver API進入了我們的視野。
關于IntersectionObserver API的官方文檔見此。兼容性如下圖所示:
簡單的說IntersectionObserver讓你知道什么時候observe的元素進入或者存在在root區域里了。下面我們來看下這個API的具體內容:
// 用構造函數生成觀察者實例,回調函數是必須的,后面的配置對象是可選的 const observer = new IntersectionObserver(changes => { for (const change of changes) { console.log(change.time); // 相交發生時經過的時間 console.log(change.rootBounds); // 表示發生相交時根元素可見區域的矩形信息,是一個對象值 console.log(change.boundingClientRect); // target.boundingClientRect()發生相交時目標元素的矩形信息,也是個對象值 console.log(change.intersectionRect); // 根元素與目標元素相交時的矩形信息 console.log(change.intersectionRatio); // 表示相交區域占目標區域的百分比,是一個0到1的值 console.log(change.target); // 相交發生時的目標元素 } }, { root: null, threshold: [0, 0.5, 1], rootMargin: "50px" }); // 實例屬性 observer.root observer.rootMargin observer.thresholds // 實例方法 observer.observe(target); // 觀察針對某個特定元素的相交事件 observer.unobserve(target); // 停止對某個特定元素的相交事件的觀察 observer.disconnect(); // 停止對所有目標元素的閾值事件的觀察,簡單的說就是停用整個IntersectionObserver // 除了上面三個實例方法,還有一個takeRecords()的方法,之后會詳細介紹
IntersectionObserver API允許開發人員了解目標dom元素相對于intersection root的可見性。這個root可以通過實例屬性獲取。默認情況下它是null,此時它不是真正意義上的元素,它指視窗范圍,因此只要視窗范圍內的目標元素滾入視窗時,就會觸發回調函數(如果root元素不存在了,則執行其任何的observe都會出錯)。
我們可以在配置對象中將root改為具體的元素,此時當目標元素出現在root元素中時會觸發回調,注意,在這種情況下相交可能發生在視窗下面。具體代碼在下,感興趣的同學可以試一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> #root { position: relative; width: 400px; height: calc(100vh + 200px); background: lightblue; overflow: scroll; } #target { position: absolute; top: calc(100vh + 800px); width: 100px; height: 100px; background: red; } </style> </head> <body> <div id="root"> <div id="target"></div> </div> <script type="text/javascript"> let ele = new IntersectionObserver( (entries) => { console.log(entries); }, { root: root } ); ele.observe(target); </script> </body> </html>
在上面的例子中,回調函數打印出來的對象中有一個intersectionRatio值,這個值其實涉及到了整個API的核心功能:當目標元素和根元素相交的面積占目標元素面積的百分比到達或跨過某些指定的臨界值時就會觸發回調函數。因此相對的在配置對象里有一個threshold來對這個百分比進行配置,默認情況下這個值是[0],注意里面的值不能在0-1之外,否則會報錯。我們舉個例子如下:
let ele = new IntersectionObserver( (entries) => { console.log(entries); }, { threshold: [0, 0.5, 1.0] } ); ele.observe(target);
在上面這個例子中,我們設定了0,0.5,1.0這三個值,因此當交叉區域跨越0,0.5,1.0時都會觸發回調函數。注意我這邊的用詞是跨越,而不是到達。因為會存在以下兩種情況導致回調打印出來的intersectionRatio不為0,0.5和1.0。
一、瀏覽器對相交的檢測是有時間間隔的。瀏覽器的渲染工作都是以幀為單位的,而IntersectionObserver是發生在幀里面的。因此假如你設定了[0,0.1,0.2,0.3,0.4,0.5]這個threshold,但是你的滾動過程特別快,導致所有的繪制在一幀里面結束了,此時回調只會挑最近的臨界值觸發一次。
二、 IntersectionObserver是異步的。在瀏覽器內部,當一個觀察者實例觀察到眾多的相交行為時,它不會立即執行。關于IntersectionObserver的草案里面寫明了其實現是基于requestIdleCallback()來異步的執行我們的回調函數的,并且規定了最大的延遲時間是100ms。關于這部分涉及到前面第一段代碼里的一個實例方法takeRecords()。如果你很迫切的希望馬上知道是否有相交,你不希望等待可能的100ms,此時你就能調用takeRecords(),此后你能馬上獲得包含IntersectionObserverEntry 對象的數組,里面有相交信息,如果沒有任何相交行為發生,則返回一個空數組。但這個方法與正常的異步回調是互斥的,如果它先執行了則正常回調里面就沒信息了,反之亦然。
除開上面的問題,如果目標元素的面積為0會產生什么情況呢?因為與0計算相交率是沒有意義的,實際我們舉個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> #target { position: relative; top: calc(100vh + 500px); width: 100px; height: 100px; background: red; } </style> </head> <body> <div id="target"></div> <div id="img"></div> <script type="text/javascript"> let ele = new IntersectionObserver( (entries) => { console.log(entries); }, { threshold: [0, 0.5, 1.0] } ); ele.observe(img); </script> </body> </html>
我們會看到,雖然我們設定了0.5這個閾值,但實際回調只會在0與1.0時觸發。這是一種特殊的處理方式。
這里需要強調一點的是,我們的目標元素在Observe的時候可以不存在的(注意這里的不存在是指沒有插入dom結構,但是元素本身是需要存在的),只需要在相交發生時存在就行了,我們來舉個栗子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> #target { position: relative; top: calc(100vh + 500px); width: 100px; height: 100px; background: red; } </style> </head> <body> <div id="target"></div> <script type="text/javascript"> let ele = new IntersectionObserver( (entries) => { console.log(entries); }, { threshold: [0, 0.5, 1.0] } ); let img = document.createElement('div'); ele.observe(img); setTimeout(() => { document.body.appendChild(img); }, 5000); </script> </body> </html>
同理,如果目標元素與根元素處于相交狀態,但是在一段時間后目標元素不存在了(比如remove,或者display:none)了,那么此時依然會觸發一次回調。但是如果本身就不處于相交狀態,然后消失掉了,因為0->0沒有變化,所以不會觸發回調,具體如下面的例子所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> #target { position: relative; top: calc(100vh + 500px); width: 100px; height: 100px; background: red; } </style> </head> <body> <div id="target"></div> <script type="text/javascript"> let ele = new IntersectionObserver( (entries) => { console.log(entries); } ); ele.observe(target); setTimeout(() => { document.body.removeChild(target); }, 5000); </script> </body> </html>
互聯網上的很多小廣告都是通過iframe嵌入的,然而現有的情況下很難獲取iframe在頂層視窗內的曝光,但是使用IntersectionObserver API我們卻可以做到這點。下面舉個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> #root { position: relative; top: calc(100vh + 800px); width: 100px; height: 100px; } #iframe { width: 600px; height: 600px; margin-bottom: 300px; } </style> </head> <body> <div id="root"> <iframe id="iframe"></iframe> </div> <script> let iframeTemplate = ` <div id="target"><p>i am iframe</p></div> <style> #target { width: 500px; height: 500px; background: red; } #target p { font-size: 90px; } </style> <script> let observer = new IntersectionObserver((entries) => { console.log(entries) }, { threshold: [0,0.5,1.0] }) observer.observe(target) </script>` iframe.src = URL.createObjectURL(new Blob([iframeTemplate], {"type": "text/html"})) </script> </body> </html>
從上面的例子可以看出,使用此API不僅能夠使iframe在視窗內出現時觸發回調,而且threshold值同樣能夠起作用。這樣一來,大大簡化了此類情況下獲取曝光的難度。
上面我們關于配置參數已經提到了root和threshold,實際上還有一個值:rootMargin。這個值實際就是給根元素添加了一個假想的margin值。使用場景最普遍的是用于延遲加載。因為如果真的等目標元素與根元素相交的時候再進行加載圖片等功能就已經晚了,所以有一個rootMargin值,這樣等于根元素延伸開去了,目標元素只要與延伸部分相交就會觸發回調,下面我們來繼續舉個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> #root { width: 500px; height: 800px; overflow: scroll; background-color: pink; } #target { position: relative; top: calc(100vh + 500px); width: 100px; height: 100px; background: red; } </style> </head> <body> <div id="root"> <div id="target"></div> </div> <script type="text/javascript"> let ele = new IntersectionObserver( (entries) => { console.log(entries); }, { rootMargin: '100px', root: root } ); ele.observe(target); </script> </body> </html>
在上面的例子中,目標元素并沒有出現在根元素的視窗里的時候就已經觸發回調了。
整個API可以用來實現無限滾動和延遲加載,下面就分別舉出兩個簡單的例子來啟發思路。
延遲加載的例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> .img { height: 1000px; overflow-y: hidden; } </style> </head> <body> <ul> <li class="img"> <img src="" class="img-item" data-src="http://okzzg7ifm.bkt.clouddn.com/cat.png"/> </li> <li class="img"> <img src="" class="img-item" data-src="http://okzzg7ifm.bkt.clouddn.com/01.png"/> </li> <li class="img"> <img src="" class="img-item" data-src="http://okzzg7ifm.bkt.clouddn.com/virtualdom.png"/> </li> <li class="img"> <img src="" class="img-item" data-src="http://okzzg7ifm.bkt.clouddn.com/reactlife.png"/> </li> </ul> <script type="text/javascript"> let ele = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.intersectionRatio > 0) { entry.target.src = entry.target.dataset.src; } }) }, { rootMargin: '100px', threshold: [0.000001] } ); let eleArray = Array.from(document.getElementsByClassName('img-item')); eleArray.forEach((item) => { ele.observe(item); }) </script> </body> </html>
無限滾動的例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>intersectionObserve</title> <style type="text/css"> .img { height: 1200px; overflow: hidden; } #flag { height: 20px; background-color: pink; } </style> </head> <body> <ul id="imgContainer"> <li class="img"> <img src="http://okzzg7ifm.bkt.clouddn.com/cat.png"/> </li> <li class="img"> <img src="http://okzzg7ifm.bkt.clouddn.com/01.png"/> </li> <li class="img"> <img src="http://okzzg7ifm.bkt.clouddn.com/virtualdom.png"/> </li> <li class="img"> <img src="http://okzzg7ifm.bkt.clouddn.com/reactlife.png"/> </li> </ul> <div id="flag"></div> <script type="text/javascript"> let imgList = [ 'http://okzzg7ifm.bkt.clouddn.com/immutable-coperation.png', 'http://okzzg7ifm.bkt.clouddn.com/flexdirection.png', 'http://okzzg7ifm.bkt.clouddn.com/immutable-exampleLayout.png' ] let ele = new IntersectionObserver( (entries) => { if (entries[0].intersectionRatio > 0) { if (imgList.length) { let newImgli = document.createElement('li'); newImgli.setAttribute("class", "img"); let newImg = document.createElement('img'); newImg.setAttribute("src", imgList[0]); newImgli.appendChild(newImg); document.getElementById('imgContainer').appendChild(newImgli); imgList.shift(); } } }, { rootMargin: '100px', threshold: [0.000001] } ); ele.observe(flag); </script> </body> </html>
通篇看下來大家是不是感覺這個API還是很好玩的,api已經問世很多年了,大部分瀏覽器都可以兼容,低版本瀏覽器可以通過Polyfill解決,規范制訂者在github上發布了Polyfill。
優點
性能比直接的監聽scroll事件或者設置timer都好
使用簡單
利用它的功能組合可以實現很多其他效果,比如無限滾動等
對iframe的支持好
缺點
它不是完美像素與無延遲的,畢竟根本上是異步的。因此不適合做滾動動畫
感謝各位的閱讀,以上就是“IntersectionObserver怎么使用”的內容了,經過本文的學習后,相信大家對IntersectionObserver怎么使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。