亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文詳解前端進(jìn)階之IntersectionObserver

 更新時(shí)間:2023年04月20日 14:14:11   作者:洞窩技術(shù)  
這篇文章主要為大家介紹了前端進(jìn)階之IntersectionObserver示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景介紹

作為一款產(chǎn)品,往往希望能得到用戶的反饋,從而通過(guò)對(duì)用戶行為的分析進(jìn)行功能、交互等方面的改進(jìn)。然而直接的一對(duì)一的用戶交流是低效且困難的,因此最普遍的做法便是通過(guò)數(shù)據(jù)埋點(diǎn)來(lái)反推用戶的行為。那么數(shù)據(jù)埋點(diǎn)中很重要的一環(huán)便是:曝光。

所謂曝光,便是頁(yè)面被展示的時(shí)候進(jìn)行打點(diǎn)。舉個(gè)簡(jiǎn)單的例子:用戶進(jìn)入分類頁(yè)面,商品以行為單位從上而下進(jìn)行排列。當(dāng)用戶滾動(dòng)頁(yè)面時(shí),之前不在視窗范圍內(nèi)的商品就會(huì)出現(xiàn),此時(shí),這部分商品就算曝光了,需要進(jìn)行一次記錄。
那么為了實(shí)現(xiàn)上面功能,最普遍的做法有兩個(gè)。其一:監(jiān)聽(tīng)滾動(dòng)事件,然后計(jì)算某個(gè)商品與視窗的相對(duì)位置,從而判斷是否可見(jiàn)。其二:設(shè)置一個(gè)定時(shí)器,然后以固定的時(shí)間為間隔計(jì)算某個(gè)商品與視窗的相對(duì)位置。

上面兩種做法在某種程度上能夠?qū)崿F(xiàn)我們的目的,但是會(huì)有一些問(wèn)題,比如最明顯的:慢。因?yàn)橛?jì)算相對(duì)位置時(shí)會(huì)調(diào)用getBoundingClientRect(),這個(gè)api會(huì)導(dǎo)致瀏覽器進(jìn)行全頁(yè)面的重新布局,影響性能,特別是在頻繁進(jìn)行時(shí)。因此IntersectionObserver API進(jìn)入了我們的視野。

IntersectionObserver API介紹

關(guān)于IntersectionObserver API的官方文檔見(jiàn)此。兼容性如下圖所示:

簡(jiǎn)單的說(shuō)IntersectionObserver讓你知道什么時(shí)候observe的元素進(jìn)入或者存在在root區(qū)域里了。下面我們來(lái)看下這個(gè)API的具體內(nèi)容:

// 用構(gòu)造函數(shù)生成觀察者實(shí)例,回調(diào)函數(shù)是必須的,后面的配置對(duì)象是可選的 
const observer = new IntersectionObserver(changes => {   
    for (const change of changes) {     
        console.log(change.time);               // 相交發(fā)生時(shí)經(jīng)過(guò)的時(shí)間
        console.log(change.rootBounds);         // 表示發(fā)生相交時(shí)根元素可見(jiàn)區(qū)域的矩形信息,是一個(gè)對(duì)象值
        console.log(change.boundingClientRect); // target.boundingClientRect()發(fā)生相交時(shí)目標(biāo)元素的矩形信息,也是個(gè)對(duì)象值
        console.log(change.intersectionRect);   // 根元素與目標(biāo)元素相交時(shí)的矩形信息     
        console.log(change.intersectionRatio);  // 表示相交區(qū)域占目標(biāo)區(qū)域的百分比,是一個(gè)0到1的值  
        console.log(change.target);             // 相交發(fā)生時(shí)的目標(biāo)元素   
       }
  }, { 	
  root: null,   	
  threshold: [0, 0.5, 1],   	
  rootMargin: "50px" 
 });  
 // 實(shí)例屬性 
 observer.root  
 observer.rootMargin  
 observer.thresholds  
 // 實(shí)例方法 
 observer.observe(target); // 觀察針對(duì)某個(gè)特定元素的相交事件  
 observer.unobserve(target); // 停止對(duì)某個(gè)特定元素的相交事件的觀察  
 observer.disconnect(); // 停止對(duì)所有目標(biāo)元素的閾值事件的觀察,簡(jiǎn)單的說(shuō)就是停用整個(gè)IntersectionObserver 
 // 除了上面三個(gè)實(shí)例方法,還有一個(gè)takeRecords()的方法,之后會(huì)詳細(xì)介紹 

IntersectionObserver API允許開(kāi)發(fā)人員了解目標(biāo)dom元素相對(duì)于intersection root的可見(jiàn)性。這個(gè)root可以通過(guò)實(shí)例屬性獲取。默認(rèn)情況下它是null,此時(shí)它不是真正意義上的元素,它指視窗范圍,因此只要視窗范圍內(nèi)的目標(biāo)元素滾入視窗時(shí),就會(huì)觸發(fā)回調(diào)函數(shù)(如果root元素不存在了,則執(zhí)行其任何的observe都會(huì)出錯(cuò))。

我們可以在配置對(duì)象中將root改為具體的元素,此時(shí)當(dāng)目標(biāo)元素出現(xiàn)在root元素中時(shí)會(huì)觸發(fā)回調(diào),注意,在這種情況下相交可能發(fā)生在視窗下面。具體代碼在下,感興趣的同學(xué)可以試一下:

<!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> 

在上面的例子中,回調(diào)函數(shù)打印出來(lái)的對(duì)象中有一個(gè)intersectionRatio值,這個(gè)值其實(shí)涉及到了整個(gè)API的核心功能:當(dāng)目標(biāo)元素和根元素相交的面積占目標(biāo)元素面積的百分比到達(dá)或跨過(guò)某些指定的臨界值時(shí)就會(huì)觸發(fā)回調(diào)函數(shù)。因此相對(duì)的在配置對(duì)象里有一個(gè)threshold來(lái)對(duì)這個(gè)百分比進(jìn)行配置,默認(rèn)情況下這個(gè)值是[0],注意里面的值不能在0-1之外,否則會(huì)報(bào)錯(cuò)。我們舉個(gè)例子如下:

let ele = new IntersectionObserver( 	
    (entries) => {   		
        console.log(entries);   
    }, {   	
        threshold: [0, 0.5, 1.0]   
    } 
);  
ele.observe(target); 

在上面這個(gè)例子中,我們?cè)O(shè)定了0,0.5,1.0這三個(gè)值,因此當(dāng)交叉區(qū)域跨越0,0.5,1.0時(shí)都會(huì)觸發(fā)回調(diào)函數(shù)。注意我這邊的用詞是跨越,而不是到達(dá)。因?yàn)闀?huì)存在以下兩種情況導(dǎo)致回調(diào)打印出來(lái)的intersectionRatio不為0,0.5和1.0。

一、瀏覽器對(duì)相交的檢測(cè)是有時(shí)間間隔的。瀏覽器的渲染工作都是以幀為單位的,而IntersectionObserver是發(fā)生在幀里面的。因此假如你設(shè)定了[0,0.1,0.2,0.3,0.4,0.5]這個(gè)threshold,但是你的滾動(dòng)過(guò)程特別快,導(dǎo)致所有的繪制在一幀里面結(jié)束了,此時(shí)回調(diào)只會(huì)挑最近的臨界值觸發(fā)一次。

二、 IntersectionObserver是異步的。在瀏覽器內(nèi)部,當(dāng)一個(gè)觀察者實(shí)例觀察到眾多的相交行為時(shí),它不會(huì)立即執(zhí)行。關(guān)于IntersectionObserver的草案里面寫明了其實(shí)現(xiàn)是基于requestIdleCallback()來(lái)異步的執(zhí)行我們的回調(diào)函數(shù)的,并且規(guī)定了最大的延遲時(shí)間是100ms。關(guān)于這部分涉及到前面第一段代碼里的一個(gè)實(shí)例方法takeRecords()。如果你很迫切的希望馬上知道是否有相交,你不希望等待可能的100ms,此時(shí)你就能調(diào)用takeRecords(),此后你能馬上獲得包含IntersectionObserverEntry 對(duì)象的數(shù)組,里面有相交信息,如果沒(méi)有任何相交行為發(fā)生,則返回一個(gè)空數(shù)組。但這個(gè)方法與正常的異步回調(diào)是互斥的,如果它先執(zhí)行了則正?;卣{(diào)里面就沒(méi)信息了,反之亦然。

除開(kāi)上面的問(wèn)題,如果目標(biāo)元素的面積為0會(huì)產(chǎn)生什么情況呢?因?yàn)榕c0計(jì)算相交率是沒(méi)有意義的,實(shí)際我們舉個(gè)例子:

<!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> 

我們會(huì)看到,雖然我們?cè)O(shè)定了0.5這個(gè)閾值,但實(shí)際回調(diào)只會(huì)在0與1.0時(shí)觸發(fā)。這是一種特殊的處理方式。

這里需要強(qiáng)調(diào)一點(diǎn)的是,我們的目標(biāo)元素在Observe的時(shí)候可以不存在的(注意這里的不存在是指沒(méi)有插入dom結(jié)構(gòu),但是元素本身是需要存在的),只需要在相交發(fā)生時(shí)存在就行了,我們來(lái)舉個(gè)栗子:

<!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> 

同理,如果目標(biāo)元素與根元素處于相交狀態(tài),但是在一段時(shí)間后目標(biāo)元素不存在了(比如remove,或者display:none)了,那么此時(shí)依然會(huì)觸發(fā)一次回調(diào)。但是如果本身就不處于相交狀態(tài),然后消失掉了,因?yàn)?->0沒(méi)有變化,所以不會(huì)觸發(fā)回調(diào),具體如下面的例子所示:

<!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> 

IntersectionObserver API與iframe

互聯(lián)網(wǎng)上的很多小廣告都是通過(guò)iframe嵌入的,然而現(xiàn)有的情況下很難獲取iframe在頂層視窗內(nèi)的曝光,但是使用IntersectionObserver API我們卻可以做到這點(diǎn)。下面舉個(gè)例子:

<!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在視窗內(nèi)出現(xiàn)時(shí)觸發(fā)回調(diào),而且threshold值同樣能夠起作用。這樣一來(lái),大大簡(jiǎn)化了此類情況下獲取曝光的難度。

延遲加載與無(wú)限滾動(dòng)

上面我們關(guān)于配置參數(shù)已經(jīng)提到了root和threshold,實(shí)際上還有一個(gè)值:rootMargin。這個(gè)值實(shí)際就是給根元素添加了一個(gè)假想的margin值。使用場(chǎng)景最普遍的是用于延遲加載。因?yàn)槿绻娴牡饶繕?biāo)元素與根元素相交的時(shí)候再進(jìn)行加載圖片等功能就已經(jīng)晚了,所以有一個(gè)rootMargin值,這樣等于根元素延伸開(kāi)去了,目標(biāo)元素只要與延伸部分相交就會(huì)觸發(fā)回調(diào),下面我們來(lái)繼續(xù)舉個(gè)例子:

<!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> 

在上面的例子中,目標(biāo)元素并沒(méi)有出現(xiàn)在根元素的視窗里的時(shí)候就已經(jīng)觸發(fā)回調(diào)了。

整個(gè)API可以用來(lái)實(shí)現(xiàn)無(wú)限滾動(dòng)和延遲加載,下面就分別舉出兩個(gè)簡(jiǎn)單的例子來(lái)啟發(fā)思路。
延遲加載的例子:

<!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> 

無(wú)限滾動(dòng)的例子:

<!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> 

通篇看下來(lái)大家是不是感覺(jué)這個(gè)API還是很好玩的,api已經(jīng)問(wèn)世很多年了,大部分瀏覽器都可以兼容,低版本瀏覽器可以通過(guò)Polyfill解決,規(guī)范制訂者在github上發(fā)布了Polyfill。

利弊介紹

  • 優(yōu)點(diǎn)

    • 性能比直接的監(jiān)聽(tīng)scroll事件或者設(shè)置timer都好
    • 使用簡(jiǎn)單
    • 利用它的功能組合可以實(shí)現(xiàn)很多其他效果,比如無(wú)限滾動(dòng)等
    • 對(duì)iframe的支持好
  • 缺點(diǎn)

    • 它不是完美像素與無(wú)延遲的,畢竟根本上是異步的。因此不適合做滾動(dòng)動(dòng)畫

以上就是一文詳解前端進(jìn)階之IntersectionObserver的詳細(xì)內(nèi)容,更多關(guān)于前端IntersectionObserver的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論