I'm trying to create a querySelectorAll
function in a MutationObserver
, so it's like calling querySelectorAll
to the newly added elements. The reason for this is so it works with other existing code.
This is proving to be hard without hard-coding selectors and using if
statements, I've thought of the following ways which all failed:
querySelectorAll
, but then it includes elements that are not just added.querySelectorAll
function and merge all the results, but it doesn't work as doesn't include the added node itself.querySelectorAll
on that element, but then the nodes disappear after the MutationObserver
runs and don't get added.Is there a way to do this, or some modification to one of the ways I've come up with so it works?
As I'm sure you know, your callback receives an array of MutationRecord
s, each of which has a NodeList
of added nodes called addedNodes
.
Turning those into a list of elements matching a selector is probably more complicated than it ideally would be, but here's one approach (see comments):
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
}
Live Example:
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>