iT邦幫忙

2024 iThome 鐵人賽

DAY 22
0
JavaScript

可愛又迷人的 Web API系列 第 22

Day22. 如何使用 MutationObserver API 追蹤 DOM 的變化 II

  • 分享至 

  • xImage
  •  

今天一樣是接續上篇,分享幾個實際使用 MutationObserver API 的情境。

監控要動態載入的內容

我們使用 AJSX 時,常常會需要等內容載入後,再執行一些操作。以往多使用 setIntervalsetTimeout 來輪詢檢查內容是否已載入,現在則可以改用 MutationObserver API 來精準地監控 DOM 的變化,當內容載入完畢後,立刻觸發一個 callback。

<div id="content"></div>
// 模擬 AJAX
setTimeout(() => {
  document.getElementById('content').innerHTML = '<p>動態載入的內容</p>';
}, 2000);

// 監控 #content 的變化
const targetNode = document.getElementById('content');
const config = { childList: true };

const callback = (mutationsList, observer) => {
  for (const mutation of mutationsList) {
    if (mutation.type === 'childList') {
      console.log('載入完畢:', mutation.addedNodes[0].textContent);
    }
  }
};

const observer = new MutationObserver(callback);
observer.observe(targetNode, config);

動態更新 UI

我們還能使用 API 動態更新網站畫面,例如當我們在網頁中動態增加或刪除元素時,可以即時更新其他相關的元素。

⬇️ 做了一個過渡的動畫,當我新增資料時,會高亮新增的資料與增加淡入的效果

<style>
  #list {
    margin-bottom: 20px;
  }
  #itemCount {
    font-weight: bold;
    margin-bottom: 20px;
  }
  p {
    opacity: 0;
    transform: translateY(-10px);
    transition: opacity 0.5s ease, transform 0.5s ease;
  }
  p.show {
    opacity: 1;
    transform: translateY(0);
  }
  p.highlight {
    background-color: yellow;
  }
</style>

<div id="itemCount">目前有 0 筆資料</div>
<div id="list"></div>
<button id="addItem">新增資料</button>
<button id="removeItem">刪除最後一筆資料</button>

⬇️ 監聽 #list,當他的子節點改變時,MutationObserver 會觸發 updateItemCount 函式,動態更新顯示在 #itemCount 中的數量。

const list = document.getElementById('list');
const addItemButton = document.getElementById('addItem');
const removeItemButton = document.getElementById('removeItem');
const itemCountDiv = document.getElementById('itemCount');

const updateItemCount = () => {
  itemCountDiv.textContent = `目前有 ${list.children.length} 筆資料`;
};

addItemButton.addEventListener('click', () => {
  const newItem = document.createElement('p');
  newItem.textContent = `資料 ${list.children.length + 1}`;
  list.appendChild(newItem);

  // 動畫效果
  requestAnimationFrame(() => {
    newItem.classList.add('show');
    newItem.classList.add('highlight');
    setTimeout(() => {
      newItem.classList.remove('highlight');
    }, 500);
  });
});

removeItemButton.addEventListener('click', () => {
  if (list.children.length > 0) {
    list.removeChild(list.lastChild);
  }
});

// 使用 MutationObserver 監聽 DOM 變更
const observer = new MutationObserver((mutationsList) => {
  for (const mutation of mutationsList) {
    if (mutation.type === 'childList') {
      updateItemCount();
    }
  }
});

observer.observe(list, { childList: true });

⬇️ 最後是這個範例的效果圖

https://mukiwu.github.io/web-api-demo/img/22-1.gif

好用但不要過度使用

寫到現在,也已經介紹了不少 Web API,大家應該能發現這些 Web API 都很好用,不過要提醒大家,雖然好用,但請不要過度使用。比如 MutationObserver,他很強大沒錯,但過度使用一定會影響網站效能,畢竟是他的本質還是監聽 DOM 元素的變化。

僅監聽必要的變化

為了避免過度監聽的狀況,我們一定要好好設定監聽的種類,就是上一章節提到的 childListattributessubtree。選對監聽的種類,避免多餘的監聽變化,可以減少不必要的消耗。

限制監聽範圍

如果開發時程很趕為了快速方便,或是初學者不太清楚 DOM 元素之間的關係,很有可能把監聽的範圍設定得很大,同樣會造成無意義的監聽浪費,所以精準的設定監聽範圍,也是很重要的一件事情。

視情況結束監聽

有開就要有關,當我們不再需要監聽 DOM 變化時,可以使用 disconnect 方法斷開這個監聽。

const stopObservingButton = document.getElementById('stopObserving');

stopObservingButton.addEventListener('click', () => {
    observer.disconnect();
    console.log('監聽已停止');
});

範例程式碼

範例程式碼網址:https://mukiwu.github.io/web-api-demo/mutation.html

小結

我還蠻喜歡用 MutationObserver API 取代 setTimeoutsetInterval ,相較這種要一直輪詢的方式,MutationObserver API 提供了更精準的方式來主動監聽 DOM 的變化,前面給了兩個範例,包括動態新增內容、動態更新 UI,這些都能提升使用者的體驗。

未來 MutationObserver API 的應用應該會愈來愈廣泛,畢竟前端有很大一個技能就是要處理網站上的元素,所以大家可以期待更多這一類的應用。

以上有任何問題,歡迎留言討論。


上一篇
Day21. 如何使用 MutationObserver API 追蹤 DOM 的變化 I
下一篇
Day23. 深入了解 Intersection Observer API 與其應用 I
系列文
可愛又迷人的 Web API28
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言