繁体   English   中英

使用变异观察器跟踪页面中的所有元素

[英]Track all elements in a page with mutation observer

我需要使用class="kendoTooltip"定位所有元素,并在具有该类的任何元素上运行 jQuery 函数。

html的一个例子是;

<div class="document_wrapper">
    <div>
        <div class="kendoTooltip">Tooltip!</div>
    </div>
</div>

我一直在尝试使用变异观察器,但无法定位页面深处的元素。

使用下面的代码,我得到了最接近的但问题似乎是变异观察者只监视音符本身及其直接子元素。 我也试过递归调用它,但没有成功。

var mutationObserver = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        if (!mutation.target.classList.contains('isRendered') && mutation.target.classList.contains('kendoTooltip')) {
            renderTooltip(mutation.target);
            mutation.target.classList.add('isRendered');
        }
    });
});

mutationObserver.observe(document.documentElement || document.body, {
    childList: true,
    subtree: true
});

注意mutation.target将是三个值之一

https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord#Properties

对于属性,它是属性改变的元素。

对于 characterData,它是 CharacterData 节点。

对于 childList,它是其子节点发生变化的节点。

因此,在您的情况下, target要么是body要么如果您的 html 实际上被添加到某个其他元素,则它将是该其他元素。 因此,您当前检查target的 classList 的代码将不起作用,因为它不是您添加的元素,而是您将它们添加到的元素。

如果您想检查已添加的元素,请使用 addedNodes 属性和搜索方法(如 querySelectorAll、getElementsByClassName 等)对您要定位的元素的 addedNodes 列表中的每个元素。

var addedNodes = Array.from(mutation.addedNodes);

addedNodes.forEach(function(node) {
  //skip textnodes
  if (node.nodeType == 3) return;
  //findall kendoTooltips that are not
  //isRendered
  var tooltips = node.querySelectorAll('.kendoTooltip:not(.isRendered)')
                     .forEach(renderTooltip);
});

 function renderTooltip(target) { console.log("tooltiping: ", target.textContent); target.classList.add("isRendered"); } var mutationObserver = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { var addedNodes = Array.from(mutation.addedNodes); addedNodes.forEach(function(node) { //skip textnodes if (node.nodeType == 3) return; //findall kendoTooltips that are not //isRendered var tooltips = node.querySelectorAll('.kendoTooltip:not(.isRendered)') .forEach(renderTooltip); }); }); }); mutationObserver.observe(document.documentElement || document.body, { childList: true, subtree: true }); setTimeout(() => { document.body.insertAdjacentHTML('beforeend', ` <div class="document_wrapper"> <div> <div class="kendoTooltip">Tooltip!</div> </div> </div>`); }, 1000); setTimeout(() => { document.body.insertAdjacentHTML('beforeend', ` <div class="anotherclass"> <div> <div class="kendoTooltip">More Tooltip!</div> </div> </div>`); }, 3000);

这是一篇旧帖子,但我想我会添加我的想法,我也有类似的问题,试图检测body元素内的元素的变化。

我并不是说我的解决方案无论如何都很好,但是看到这个问题没有公认的答案,也许我的想法可以帮助找到解决方案。

我的问题是我想创建一个 JavaScript 解决方案来检测 ReactJS 应用程序使用的<div id="root"></div>元素内的插入,我为什么要这样做的原因并不重要。

我发现将MutationObserver附加到documentdocument.documentElementdocument.body并没有提供任何解决方案。 我最终不得不将我的MutationObserver放在一个循环中,并将其附加到body每个子元素,以便我可以检测到变化。

这是我的代码,我确信有更好的方法可以做到这一点,但我还没有找到:

// Public function
export function listenForDynamicComponents() {

    // Create the observer
    const observer = new MutationObserver((mutations) => {

        // Loop each mutation
        for (let i = 0; i < mutations.length; i++) {
            const mutation = mutations[i];

            // Check the mutation has added nodes
            if (mutation.addedNodes.length > 0) {
                const newNodes = mutation.addedNodes;

                // Loop though each of the added nodes
                for (let j = 0; j < newNodes.length; j++) {
                    const node = newNodes[j];

                    // Check if current node is a component
                    if (node.nodeType === 1 && node.hasAttribute('data-module')) {
                        // Do something here
                    }

                    // Loop though the children of each of the added nodes
                    for (let k = 0; k < node.childNodes.length; k++) {
                        const childNode = node.childNodes[k];

                        // Check if current child node is a compoennt
                        if (childNode.nodeType === 1 && childNode.hasAttribute('data-module')) {
                            // Do something here
                        }
                    }
                }
            }
        }
    });

    // Get all nodes within body
    const bodyNodes = document.body.childNodes;

    // Loop though all child nodes of body
    for (let i = 0; i < bodyNodes.length; i++) {
        const node = bodyNodes[i];

        // Observe on each child node of body
        observer.observe(node, {
            attributes: false,
            characterData: false,
            childList: true,
            subtree: true,
            attributeOldValue: false,
            characterDataOldValue: false
        });
    }
}

我希望这有帮助。

根据记录,与某些评论中所说的相反, subtree选项将针对观察元素的所有后代:孩子,孩子的孩子......但仅限于直接从 DOM 添加或删除的那些。 请参阅 此处此处 顺便说一下,有一个陷阱。 想象一下,你想要观察一个video标签,它不是直接附加到 DOM 而是附加到一个分离的 DOM,比如div.video-container ,并且它是添加到 DOM 的容器。

const mutationInstance = new MutationObserver((mutations) => {
  // The <video> is not directly added to the DOM, so wee need to query it on each added nodes
  const videoContainer = mutations
    .reduce((addedNodes, mutationRecord) => {
      return [
        ...addedNodes,
        ...(Array.from(mutationRecord.addedNodes)),
      ];
    }, [])
    .find((element) => {
      return element.querySelector("video") !== null;
    });
   
   const videoElement = videoContainer.querySelector("video");
   console.log('Video element is', videoElement);
});

mutationInstance.observe(document, {
  subtree: true,
  childList: true,
});

触发突变的代码

const div = document.createElement('div');
div.classList.add('video-container');
const video = document.createElement('video');

// This **does not trigger** a mutation on the document
div.appendChild(video);

// This **triggers** a mutation on the document
document.body.appendChild(div);

我很确定你不能像在事件中那样委派MutationObserver (尽管公平地说,我从未真正尝试过)。 我认为您需要为每个元素创建一个新元素。

document.querySelectorAll(".kendoTooltip").forEach(function (tooltip) {

    var mutationObserver = new MutationObserver(function (mutations) {
        // ...
    });

    mutationObserver.observe(tooltip, {
        // ...
    });

});

就其价值而言,在添加isRendered类时触发该renderTooltip函数会容易isRendered而不必担心MutationObserver

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM