能夠先看一下MDN
中的介紹:css
IntersectionObserver
接口,提供了一種異步觀察目標元素與其祖先元素或頂級文檔視窗(viewport)交叉狀態
的方法,祖先元素與視窗(viewport
)被稱爲根(root
);html
直接進入正題,IntersectionObserver
翻譯爲 "交叉觀察者
",它的任務就是監聽目標元素
跟指定父元素
(用戶可指定,默認爲viewport
)是否在發生交叉行爲
,簡單理解就是監聽目標元素
是否進入或者離開了指定父元素
的內部(理解這句就好了,管他交不交叉呢),我好像在開車,可是大家沒有證據 ... 😐前端
如下的目標元素
簡稱爲目標
、指定父元素
簡稱爲父親
、交叉行爲
簡稱爲交叉
,viewport
簡稱爲視窗
👌git
下面會有動圖介紹,先忍忍!github
new IntersectionObserver(callback, options);
發生交叉
的回調,接受一個entries
參數,返回當前已監聽
而且發生了交叉
的目標
集合(後面會舉例說明爲何是"且發生了交叉
"):瀏覽器
new IntersectionObserver(entries => { entries.forEach(item => console.log(item)); // ... });
咱們看看item
裏面包含哪些經常使用
屬性:微信
屬性 | 說明 | |
---|---|---|
boundingClientRect | 空間信息 | |
intersectionRatio | 元素可見區域的佔比 | |
isIntersecting | 字面理解爲是否正在交叉 ,可用作判斷元素是否可見 |
|
target | 目標節點,就跟event.target 同樣 |
注意:頁面初始化的時候會觸發一次callback
,entries
爲全部已監聽的目標集合
✅異步
顧名思義,它是一個配置
參數,對象類型,非必填,經常使用
屬性以下:函數
屬性 | 說明 | |
---|---|---|
root | 指定父元素,默認爲視窗 |
|
rootMargin | 觸發交叉 的偏移值,默認爲"0px 0px 0px 0px"(上左下右,正數爲向外擴散,負數則向內收縮) |
new IntersectionObserver(callback, { root: document.querySelector("xx"), rootMargin: "0px 0px -100px 0px" });
若是設置rootMargin
爲"20px 0px 30px 30px
",那麼元素未到達視窗
時,就已經切換爲可見
狀態了:學習
名稱 | 說明 | 參數 |
---|---|---|
observe | 開始監聽一個目標元素 | 節點 |
unobserve | 中止監聽一個目標元素 | 節點 |
takeRecords | 返回全部監聽的目標元素集合 | |
disconnect | 中止全部監聽 |
class="box"
的盒子且父元素爲視窗
:let box = document.querySelector(".box"); let observer = new IntersectionObserver(entries => { entries.forEach(item => { let tips = item.isIntersecting ? "進入了父元素的內部" : "離開了父元素的內部"; console.log(tips); }); }); observer.observe(box); // 監聽一個box
效果以下:
class="box"
的盒子且父元素爲視窗
:let box = document.querySelectorAll(".box"); let observer = new IntersectionObserver(entries => console.log(`發生交叉行爲,目標元素有${entries.length}個`)); box.forEach(item => observer.observe(item)); // 監聽多個box
當全部盒子距離視窗頂部距離一致
時,效果以下:
當全部盒子距離視窗頂部距離不一致
時,效果以下:
爲何要舉例
以上兩種狀況呢,由於entries
是返回當前已監聽
而且發生了交叉
的目標集合
,第一種狀況,你們都一塊兒
發生交叉
,固每次返回的集合長度都爲三
;第二種狀況則是每一個目標輪流
發生交叉
,且當前只觸發了一個
,因此每次返回的集合長度只有一
✅
假設html
以下:
<div class="parent"> <div class="child"></div> </div>
而後開始監聽:
let child = document.querySelector(".child"); let observer = new IntersectionObserver(entries => { entries.forEach(item => { console.log(item.isIntersecting ? "可見" : "不可見"); }); }, { root: document.querySelector(".parent") }); observer.observe(child); // 開始監聽child
效果以下:
之前都是監聽瀏覽器滾動,而後遍歷拿到每一個圖片的空間信息,而後判斷一些位置信息從而進行圖片加載;而如今只須要交給交叉觀察者
去作:
let images = document.querySelectorAll("img.lazyload"); let observer = new IntersectionObserver(entries => { entries.forEach(item => { if (item.isIntersecting) { item.target.src = item.target.dataset.origin; // 開始加載圖片 observer.unobserve(item.target); // 中止監聽已開始加載的圖片 } }); }); images.forEach(item => observer.observe(item));
效果以下:
把網速調慢:
設置rootMargin
偏移值爲"0px 0px -100px 0px
"(底部向內收縮):
該方法還有一個好處,那就是當頁面上某個節點存在橫向滾動條的時候,同樣應對自如:
傳統的懶加載只是監聽全局滾動條的滾動,像這種小細節仍是沒法實現的(傳統的實現方法並非判斷目標是否出如今視窗
,因此橫向的圖片會一塊兒加載,即便你沒有向左滑動),因此這也是交叉觀察者
的一大優勢✅
咱們在列表底部放一個參照元素
,而後讓交叉觀察者
去監聽;
假設html
結構以下:
<!-- 數據列表 --> <ul> <li>index</li> </ul> <!-- 參照元素 --> <div class="reference"></div>
而後監聽參照元素:
new IntersectionObserver(entries => { let item = entries[0]; // 拿第一個就行,反正只有一個 if (item.isIntersecting) console.log("滾動到了底部,開始請求數據"); }).observe(document.querySelector(".reference")); // 監聽參照元素
效果以下:
實現元素吸頂的方式有不少種,如css的position: sticky
,兼容性較差;若是用交叉觀察者
實現也很方便,一樣也要放一個參照元素
;
假設html
結構以下:
<!-- 參照元素 --> <div class="reference"></div> <nav>我能夠吸頂</nav>
假設scss
代碼以下:
nav { &.fixed { position: fixed; top: 0; left: 0; width: 100%; } }
開始監聽:
let nav = document.querySelector('nav'); let reference = document.querySelector(".reference"); new IntersectionObserver(entries => { let item = entries[0]; let top = item.boundingClientRect.top; // 當參照元素的的top值小於0,也就是在視窗的頂部的時候,開始吸頂,不然移除吸頂 if (top < 0) nav.classList.add("fixed"); else nav.classList.remove("fixed"); }).observe(reference);
效果以下:
可是有個問題,當你滾動的慢的時候,會掉進一個死循環:
爲了方便觀察,咱們把參考元素加一個高度跟顏色:
問題很明顯,當給nav
增長fixed
定位時,nav
脫離了文檔流,天然參考元素
會往下掉,而後往下掉又發生了交叉
,從而去除fixed
定位,陷入一個死循環;
思考了一會,解決辦法是,讓參考元素
絕對定位至nav
的上方:
let nav = document.querySelector('nav'); let reference = document.querySelector(".reference"); reference.style.top = nav.offsetTop + "px"; // 如下代碼不變 ...
這樣,即便nav
脫離的文檔流,也不會影響參考元素
的位置:
相信不少人都須要過這種需求,當某個元素出現的時候就給該元素加個動畫,好比漸變、偏移等;
假設html
結構以下:
<ul> <li></li> </ul>
假設scss
代碼以下:
ul { li { &.show { // 默認從左邊進來 animation: left 1s ease; // 偶數從右邊進來 &:nth-child(2n) { animation: right 1s ease; } } } } @keyframes left { from { opacity: 0; transform: translate(-20px, 20px); // right動畫改爲20px, 20px便可 } to { opacity: 1; } }
而後開始監聽:
let list = document.querySelectorAll("ul li"); let observer = new IntersectionObserver(entries => { entries.forEach(item => { if (item.isIntersecting) { item.target.classList.add("show"); // 增長show類名 observer.unobserve(item.target); // 移除監聽 } }); }); list.forEach(item => observer.observe(item));
效果以下:
IE不兼容,不過有官方的polyfill✅
暫時就發現這麼多用途啦,值得注意的是,必須是子元素跟父元素髮生交叉
,若是你想檢查兩個非父子關係的交叉
,那是不行
的嘻嘻,若是你以爲這篇文章不錯,請別忘記點個贊
跟關注
哦~😊
公衆號「前端宇宙情報局」
,將不定時更新最新、實用的前端技巧/技術性文章,對了偶爾還會有互聯網中的趣事趣聞🍻
關注公衆號,回覆"1
"獲取微信羣聊二維碼,一塊兒學習、一塊兒交流、一塊兒摸魚🌊