как использовать querySelectorAll на добавленных узлах в MutationObserver - PullRequest
1 голос
/ 15 июня 2019

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

Это оказывается трудным без жестко кодирующих селекторов и использования if операторов, я думал о следующих путях, которые все потерпели неудачу:

  • Попробуйте использовать родительский номер добавленного узла querySelectorAll, но тогда он включает элементы, которые не просто добавляются.
  • Используйте функцию добавленного узла querySelectorAll и объедините все результаты, но она не работает, поскольку не включает сам добавленный узел.
  • Создайте новый элемент, переместите в него все добавленные узлы и вызовите querySelectorAll для этого элемента, но затем узлы исчезают после выполнения MutationObserver и не добавляются.

Есть ли способ сделать это или какая-то модификация одного из предложенных мной способов, чтобы он работал?

Ответы [ 2 ]

1 голос
/ 15 июня 2019

Как я уверен, вы знаете, что ваш обратный вызов получает массив 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
}

Live Пример:

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>
1 голос
/ 15 июня 2019

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 соответствуют селектору, а не только самим 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.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);
<div class="somediv">existing</div>
...