有没有一种方法可以通过 CSS 选择器来观察元素的 DOM 突变?如果可以,该怎么做?

Is there a way to observe a DOM mutation of an element by a CSS selector and if so, how to do so?

某个网站有某个 HTML 元素,如果我理解正确的话,在 DOMContentLoaded 事件和 load 事件中有相同的 class,但在 load 事件发生后的某个时间,此 class(可能还有 ID 和 HTML 属性)发生了变化。

我想 "observe" 这个元素从它存在于 DOM 的第一刻开始,以便自动跟随它 HTML 可能有的任何变化。

有没有办法通过 CSS 选择器来观察元素的 DOM 突变?如果可以,该怎么做?

我问这个假设我正确理解了 JavaScript 中的 "observation" 概念。

Is there a way to observe a DOM mutation of an element by a CSS selector...

有点。您可以在元素及其后代元素中观察 所有 修改,并在您看到添加或修改的元素上使用 mutation observer, and you can use the matches 方法查看它们是否匹配给定的 CSS选择器(或在您正在观看的容器上使用 querySelector)。

结合突变观测器和matches/querySelector(All),您可以看到任何您需要看到的变化。

// Get the container
let container = document.getElementById("container");

// The selector we're interested in
let selector = "div.foo";

// Set up a mutation observer
let observer = new MutationObserver(records => {
    // A mutation occurred within the container.
    // `records` contains the information about the mutation(s)

    // If you're looking to see if a new element matching the selector
    // was *added*, you can loop through the added nodes (if any)
    for (const record of records) {
        for (const added of record.addedNodes) {
            if (added.nodeType === Node.ELEMENT_NODE && added.matches(selector)) {
                console.log("Matching element added: " + added.textContent);
            }
        }
    }

    // If you're looking to see if an element *changed* to match the
    // selector, you can look at the target of each record
    for (const {target} of records) {
        if (target.nodeType === Node.ELEMENT_NODE && target.matches(selector)) {
            console.log("Element changed to match: " + target.textContent);
        }
    }

    // Or alternatively ignore the records and *why* an element now matches,
    // and just see if any does.
    const found = container.querySelectorAll(selector);
    if (found.length) {
        console.log("Matching elements found: " + found.length);
        for (const {textContent} of found) {
            console.log("Matching element found: " + textContent);
        }
    }
});
observer.observe(container, {
    // Tweak what you're looking for depending on what change you want to find.
    childList: true,
    subtree: true,
    attributes: true
});

// Add five div elements; the fourth will have the
// class we're interested in.
let counter = 0;
let timer = setInterval(() => {
    const div = document.createElement("div");
    ++counter;
    div.textContent = "Div #" + counter;
    if (counter === 4) {
        div.className = "foo";
    }
    console.log("Adding div: " + div.textContent);
    container.appendChild(div);
    if (counter === 5) {
        clearInterval(timer);
    }
}, 200);

// After 1200ms, *change* one of the divs to match the selector
setTimeout(() => {
    const div = container.querySelector("div:not(.foo)");
    console.log("Changing element to match: " + div.textContent);
    div.className = "foo";
}, 1200);
<div id="container">
    This is the container.
</div>

当然,您需要调整传递给观察者的选项和观察者回调中的代码以匹配您的场景。 :-)