我试图在querySelectorAll
中创建一个MutationObserver
函数,所以就像对新添加的元素调用querySelectorAll
一样。原因是它可以与其他现有代码一起使用。
如果没有对选择器进行硬编码并使用if
语句,这很难做到,我想到了以下所有失败的方法:
querySelectorAll
,但是它包含的元素并不只是添加的。querySelectorAll
函数并合并所有结果,但是由于不包括添加的节点本身,因此不起作用。querySelectorAll
,但是这些节点在MutationObserver
运行后消失,并且没有添加。有没有办法做到这一点,或者对我想出的一种修改方式使其起作用?
答案 0 :(得分:1)
addedNodes
是一个NodeList集合。您可以通过调用querySelectorAll
来实现与Array.prototype.filter
几乎相同的功能,其中回调函数检查选择器是否通过了给定元素.matches
的传递:
new MutationObserver((mutationsList) => {
const { addedNodes } = mutationsList[0];
const matches = [...addedNodes]
.filter(node => node.nodeType === 1)
.filter(element => element.matches('.someDiv'));
if (matches.length) {
console.log(matches);
}
})
.observe(document.body, { childList: true });
setTimeout(() => {
document.body.insertAdjacentHTML(
'beforeend',
`<div class="someDiv">dynamically added matching</div>
<div class="nonMatching">dynamically added non-matching</div>`
);
}, 1000);
<div class="somediv">existing</div>
只需将要传递给.matches
的选择器字符串替换为您要过滤的选择器字符串即可。
如果要检查addedNodes
的 any 个子元素是否与选择器匹配,而不仅仅是addedNodes
本身,则可以使用{{1} },以便从每个元素中提取子匹配项数组:
flatMap
new MutationObserver((mutationsList) => {
const { addedNodes } = mutationsList[0];
const elements = [...addedNodes]
.filter(node => node.nodeType === 1);
const matches = [
...elements.filter(element => element.matches('.someDiv')),
...elements.flatMap(element => [...element.querySelectorAll('.someDiv')])
];
if (matches.length) {
console.log(matches);
}
})
.observe(document.body, { childList: true });
setTimeout(() => {
document.body.insertAdjacentHTML(
'beforeend',
`<div class="someDiv">dynamically added matching
<div class="someDiv">dynamically added matching nested</div>
</div>
<div class="nonMatching">dynamically added non-matching</div>`
);
}, 1000);
答案 1 :(得分:1)
我确定您知道,您的回调函数收到一个MutationRecord
个数组,每个数组都有NodeList
个添加的节点,称为addedNodes
。
将其转换为与选择器匹配的元素列表可能比理想情况下要复杂得多,但这是一种方法(请参阅注释):
function applySelector(selector, records) {
// We can't create a NodeList; let's use a Set
const result = new Set();
// Loop through the records...
for (const {addedNodes} of records) {
for (const node of addedNodes) {
// If it's an element...
if (node.nodeType === 1) {
// Add it if it's a match
if (node.matches(selector)) {
result.add(node);
}
// Add any children
addAll(result, node.querySelectorAll(selector));
}
}
}
return [...result]; // Result is an array, or just return the set
}
实时示例:
const ob = new MutationObserver(records => {
const result = applySelector("span", records);
console.log(`Got ${result.length} matches:`);
for (const span of result) {
console.log(span.id);
}
});
const target = document.getElementById("target");
ob.observe(target, {childList: true});
target.insertAdjacentHTML(
"beforeend",
`<div>
blah
<span id="span1">span</span>
blah
<div>
<span id="span2">lorem <span id="span3">ipsum</span></span>
</div>
</div>`
);
function addAll(set, list) {
for (const entry of list) {
set.add(entry);
}
}
function applySelector(selector, records) {
// We can't create a NodeList; let's use a Set
const result = new Set();
// Loop through the records...
for (const {addedNodes} of records) {
for (const node of addedNodes) {
// If it's an element...
if (node.nodeType === 1) {
// Add it if it's a match
if (node.matches(selector)) {
result.add(node);
}
// Add any children
addAll(result, node.querySelectorAll(selector));
}
}
}
return [...result]; // Result is an array, or just return the set
}
<div id="target"></div>