能夠先看一下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
裏面包含哪些經常使用
屬性:bash
屬性 | 說明 |
---|---|
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
複製代碼
效果以下:
之前都是監聽瀏覽器滾動,而後遍歷拿到每一個圖片的空間信息,而後判斷一些位置信息從而進行圖片加載;而如今只須要交給交叉觀察者
去作;
假設html
結構以下:
// 多個
<img src="" data-origin="圖片連接">
複製代碼
而後開始監聽:
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
"獲取微信羣聊
二維碼,一塊兒學習、一塊兒交流、一塊兒摸魚🌊