面試官:MutationObserver與IntersectionObserver 傻傻分不清楚?
Hello,大家好,我是 Sunday。
昨天,有位同學(xué)在面試的時候被問到:“MutationObserver 和 IntersectionObserver 的差異以及作用場景”。
所以,咱們今天就把這塊給大家統(tǒng)一解釋一下!
1. MutationObserver 和 IntersectionObserver 的區(qū)別
MutationObserver
- 作用:用于監(jiān)聽 DOM 樹的變動,包括:元素的屬性、子元素列表或節(jié)點文本的變化。
- 適用場景:可以用來檢測 DOM 的結(jié)構(gòu)和內(nèi)容變化,比如元素被插入或刪除、屬性被更改等。
- 性能:由于 MutationObserver 監(jiān)聽的是整個 DOM 樹的變化,頻繁的 DOM 操作會導(dǎo)致性能問題,因此適用于較少變化的場景。
IntersectionObserver
- 作用:用于監(jiān)聽目標(biāo)元素與其祖先元素(或 viewport)之間的交叉狀態(tài),即是否進(jìn)入或離開視口。
- 適用場景:適合用于檢測元素是否在視口中,例如:實現(xiàn)圖片懶加載、無限滾動或曝光監(jiān)測。
- 性能:由于它的監(jiān)聽目標(biāo)是元素的可見性,相較于 MutationObserver,更適合頻繁變化的場景。
特性 | MutationObserver | IntersectionObserver |
監(jiān)聽對象 | DOM 節(jié)點的結(jié)構(gòu)、屬性或文本變化 | 目標(biāo)元素與視口或指定元素的交叉狀態(tài) |
常見使用場景 | 檢測 DOM 變化(插入、刪除、修改) | 圖片懶加載、曝光監(jiān)測、滾動加載等 |
性能 | 頻繁變化可能影響性能 | 更適合高頻率變化的監(jiān)聽 |
2. 應(yīng)用場景
IntersectionObserver
在之前我們做圖片懶加載的時候,其實是使用過 IntersectionObserver 的。我們會使用它檢測 DOM(img) 是否可見,以此來判斷是否需要加載對應(yīng)的圖片:
// 懶加載圖片的回調(diào)函數(shù),包含淡入效果和錯誤處理
function lazyLoadImages(entries, observer) {
entries.forEach(entry => {
// 檢查圖片是否進(jìn)入視口
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src; // 將 data-src 替換為 src 開始加載圖片
// 圖片加載成功后,添加 'loaded' 類觸發(fā)淡入效果
img.onload = () => img.classList.add('loaded');
// 圖片加載失敗時,顯示默認(rèn)占位圖
img.onerror = () => img.src = 'placeholder.jpg';
observer.unobserve(img); // 停止觀察該圖片
}
});
}
// 創(chuàng)建 IntersectionObserver 實例,用于懶加載
const imageObserver = new IntersectionObserver(lazyLoadImages, { threshold: 0.1 });
// 選取所有帶有 data-src 屬性的圖片并開始觀察
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
除此之外,IntersectionObserver 在 性能檢測 中也有應(yīng)用場景。
比如昨天,我們講解的 前端埋點與監(jiān)控最佳實踐:從基礎(chǔ)到全流程實現(xiàn) 里,就可以通過 IntersectionObserver 來完成 【曝光監(jiān)測】的功能:
trackEvent 方法參考 前端埋點與監(jiān)控最佳實踐:從基礎(chǔ)到全流程實現(xiàn)
// 處理元素可見性變化的回調(diào)函數(shù)
function handleIntersection(entries, observer) {
entries.forEach(entry => {
// 檢查元素是否進(jìn)入視口
if (entry.isIntersecting) {
console.log('元素已進(jìn)入視口:', entry.target);
// 調(diào)用自定義追蹤事件函數(shù),記錄元素可見性
trackEvent('element_visible', { elementId: entry.target.id });
// 可選:停止觀察該元素(僅觸發(fā)一次)
observer.unobserve(entry.target);
}
});
}
// 創(chuàng)建 IntersectionObserver 實例
const observer = new IntersectionObserver(handleIntersection, {
root: null, // 使用視口作為容器
threshold: 0.5 // 當(dāng)元素 50% 可見時觸發(fā)回調(diào)
});
// 選擇需要觀察的目標(biāo)元素
const targetElement = document.getElementById('target');
observer.observe(targetElement);
// 示例追蹤事件函數(shù)
function trackEvent(eventType, details) {
console.log(`記錄事件: ${eventType}`, details);
// 在這里將追蹤數(shù)據(jù)發(fā)送到服務(wù)器或分析服務(wù)
}
MutationObserver
MutationObserver 主要 監(jiān)聽 DOM 的動態(tài)變化(添加、刪除 等)。在 SPA 應(yīng)用中,動態(tài)加載的場景下會非常有用。
比如,我們做一個評論提交的功能,當(dāng)用戶提交一條新評論時,我們希望檢測到 DOM 變化并觸發(fā)相關(guān)操作:
<div id="comments-section">
<p>評論列表:</p>
<div id="comments">
<p>用戶1: 很棒的文章!</p>
</div>
</div>
<button onclick="addComment()">添加評論</button>
<script>
// 模擬添加評論
function addComment() {
const comment = document.createElement("p");
comment.textContent = `用戶${Date.now()}: 新的評論內(nèi)容`;
document.getElementById("comments").appendChild(comment);
}
// MutationObserver 實例
const commentsSection = document.getElementById("comments");
const observer = new MutationObserver((mutationsList) => {
mutationsList.forEach((mutation) => {
if (mutation.type === 'childList') {
// 調(diào)用自定義追蹤事件函數(shù),記錄元素可見性
trackEvent('element_update', { elementId: target.target.id });
}
});
});
// 觀察評論區(qū)的子節(jié)點變化
observer.observe(commentsSection, {
childList: true, // 監(jiān)聽子節(jié)點變化
});
</script>