Я пытаюсь создать функцию querySelectorAll в MutationObserver, так что это похоже на вызов querySelectorAll для вновь добавленных элементов. Причина этого в том, что он работает с другим существующим кодом.
Это оказывается сложным без жестко запрограммированных селекторов и использования операторов if, я подумал о следующих способах, которые все потерпели неудачу:
querySelectorAll родителя добавленного узла, но тогда он включает элементы, которые не были просто добавлены.querySelectorAll и объедините все результаты, но она не работает, так как не включает сам добавленный узел.querySelectorAll для этого элемента, но затем узлы исчезнут после запуска MutationObserver и не будут добавлены.Есть ли способ сделать это или какая-то модификация одного из способов, которые я придумал, чтобы он работал?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


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.info(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 селектору, а не только самим addedNodes, вы можете использовать что-то вроде 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.info(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);<div class = "somediv">existing</div>Как насчет совпадающих элементов-потомков добавленных элементов? :-)
Хм, может быть, я не совсем ясно объяснил, но цель состоит в том, чтобы иметь функцию, которая проксирует querySelectorAll, я уже пытался использовать filter, текущее жестко запрограммированное решение использует генератор.
@simonzack querySelectorAll работает только с документом или элементом — он не работает с NodeList, поэтому вы не можете использовать его напрямую — что-то вроде .filter и .matches — лучшая альтернатива.
@simonzack - Хитрость в том, что, поскольку обратный вызов наблюдателя получает массив записей, которые затем имеют NodeLists добавленных узлов, вам в основном нужно добавить querySelectorAll к Array.prototype, что, вероятно, не было бы идеальным. :-)
Я уверен, вы знаете, что ваш обратный вызов получает массив 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.info(`Got ${result.length} matches:`);
for (const span of result) {
console.info(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>Хм, спасибо, я думаю, это немного сложнее, чем я думал, но это работает. Спасибо, что рассказали мне о функции .matches, по крайней мере, это позволяет избежать жесткого кодирования.
@simonzack - Да, matches это здорово. :-)
@T.J.Crowder Я попробую, но это немного сложно, так как это расширение для браузера Google.