[英]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
附加到document
、 document.documentElement
或document.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.