Hi 🤓 Cảm ơn bạn đã ghé thăm blog này, nếu những bài viết trên blog giúp ích cho bạn. Bạn có thể giúp blog hiển thị quảng cáo bằng cách tạm ngừng ad blocker 😫 và để giúp blog duy trì hoạt động nếu bạn muốn.
Cảm ơn bạn!

Trong bài viết này, chúng ta cùng tìm hiểu cách để xem thay đổi DOM, ví dụ như biết được Node đã được thêm vào DOM hay đã bị xóa, thuộc tính của nó có bị thay đổi hay không với MutationObserver trong JavaScript.

MutationObserver trong JavaScript

MutationObserver là một API cho phép chúng ta quan sát các thay đổi đối với DOM. Khi node đang quan sát thay đổi, bạn có thể sử dụng một callback function để làm điều mà bạn muốn 🕵️‍♀️.

Các bước cơ bản để chúng ta sử dụng MutationObserver API là:

Tạo một callback function, nó sẽ được sử dụng khi DOM bị thay đổi.

function callback(mutations, observer) {
    // ...
}

Tiếp theo, tạo một MutationObserver object và truyền callback vào MutationObserver constructor.

const observer = new MutationObserver(callback);

Sử dụng method observe để bắt đầu quan sát thay đổi DOM.

observer.observe(target, options);

👉 Method observe có hai tham số:

  • target là một Node để quan sát thay đổi.
  • options là một object chứa thuộc tính chỉ định những thay đổi nào của DOM sẽ được báo cho callback của observer.

Dưới đây là một số thuộc tính có thể thêm vào options:

Options Description Default Value
subtree Theo dõi toàn bộ các node con của target, các thuộc tính khác cũng sẽ được áp dụng cho node con. false
childList Theo dõi khi thêm/xóa các node con của target (áp dụng cho cả subtree khi nó được set là true). false
attributes Theo dõi các thay đổi của giá trị ở các thuộc tính (attributes) của node true nếu attributeFilter hoặc attributeOldValue được chỉ định, nếu không mặc định là false.
attributeFilter Mảng chứa các thuộc tính để theo dõi. Nếu không thêm vào các thuộc tính, tất cả thuộc tính sẽ được theo dõi. Không có
attributeOldValue Đặt thành true để ghi lại giá trị trước đó của thuộc tính được theo dõi false
characterData Theo dõi các thay đổi đối với text của target (áp dụng cho cả subtree khi nó được set là true). true nếu characterDataOldValue được chỉ định, nếu không mặc định là false.
characterDataOldValue Ghi lại giá trị trước đó của text khi nó được thay đổi ở các node được theo dõi. false

Chúng ta không cần phải sử dụng tất cả các tùy chọn ở trên. Tuy nhiên, để MutationObserver hoạt động, ta cần sử dụng ít nhất một trong các thuộc tính là childList, attributes, characterData (đặt thành true), nếu không observer() sẽ báo lỗi.

Cùng xem ví dụ dưới đây để hiểu cách hoạt động của observer() nhé.

Chúng ta sẽ giả lập một tình huống thêm element vào DOM, thay đổi attributes cho element vừa thêm.

<div id="homiedev" title="homiedev" data-url="https://homiedev.com">
  homiedev.com
  <ul class="posts">
    <li title="first-post">
      <a href="https://homiedev.com/hoc-javascript-co-ban"
         >Học JavaScript</a
        >
    </li>
  </ul>
</div>
<div class="actions">
  <button type="button" id="add-an-element">Thêm một element</button>
  <button type="button" id="change-title">
    Đổi title cho element vừa thêm
  </button>
  <button type="reset" id="reset">Reset</button>
</div>

Ở HTML, tạo ra 3 button với nhiệm vụ: thêm element vào DOM, đổi title cho element vừa tạo, reset.

Element mới sẽ được thêm vào posts.

Phần code JavaScript của chúng ta sẽ như sau:

const homiedevEle = document.getElementById("homiedev");
const postsEle = document.querySelector(".posts");
const addAnElementBtn = document.getElementById("add-an-element");
const changeTitleBtn = document.getElementById("change-title");
const resetBtn = document.querySelector("[type='reset']");

let count = 0;

const observer = new MutationObserver(function (mutations, observer) {
  // console.log(mutations, observer);

  let changes = "Những thay đổi: ";

  mutations.forEach(function (mutation) {
    switch (mutation.type) {
      case "childList": {
        if (mutation.addedNodes.length) changes += "Thêm một element; ";
        else if (mutation.removedNodes.length)
          changes += "Xóa một element; ";

        break;
      }
      case "attributes": {
        const { attributeName, oldValue, target } = mutation;

        changes += `Giá trị ${attributeName} của <${target.localName}>: "${oldValue}" => "${target.title}"; `;
        break;
      }

      default:
        break;
    }
  });

  window.alert(changes);
});

observer.observe(homiedevEle, {
  attributeFilter: ["title"],
  attributeOldValue: true,
  characterDataOldValue: true,
  childList: true,
  subtree: true,
});

addAnElementBtn.addEventListener("click", (e) => {
  const title = `Bài viết ${++count}`;

  postsEle.insertAdjacentElement(
    "beforeend",
    createElement("li", { innerText: title, title })
  );
});

changeTitleBtn.addEventListener("click", (e) => {
  const { lastElementChild } = postsEle;

  lastElementChild?.setAttribute("title", "Đã đổi title 😁!");
});

resetBtn.addEventListener("click", (e) => {
  location.reload();
});

function createElement(tagName, properties) {
  return Object.assign(document.createElement(tagName), properties);
}

options trong đoạn code trên gồm những thuộc tính:

{
  attributeFilter: ["title"],
  attributeOldValue: true,
  characterDataOldValue: true,
  childList: true,
  subtree: true,
}

Với thuộc tính attributeOldValuecharacterDataOldValue khi set là true thì mặc định giá trị của attributescharacterData cũng được set thành true. Khi giá trị của attribute title thay đổi thì nó sẽ thực thi hàm trong observer.

Khi node con được thêm vào DOM, ta cũng sẽ nhận được thông báo thay đổi vì ta đã set thuộc tính childListsubtreetrue.

Bạn có thể test thử code ở trên tại đây.

Cuối cùng nếu bạn muốn hủy theo dõi thay đổi DOM thì có thể sử dụng:

observer.disconnect();

Vừa rồi chúng ta đã tìm hiểu cách xem những thay đổi từ DOM với MutationObserver trong JavaScript. Hi vọng bài viết giúp ích cho các bạn.

Cám ơn các bạn đã đọc bài viết ^^.

Có thể bạn thích ⚡
homiedev
About Me

Hi, I'm @devnav. Một người thích chia sẻ kiến thức, đặc biệt là về Frontend 🚀. Trang web này được tạo ra nhằm giúp các bạn học Frontend hiệu quả hơn 🎉😄.

Chúc các bạn tìm được kiến thức hữu ích trong blog này 😁😁.