Как использовать querySelectorAll для добавленных узлов в MutationObserver

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

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

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

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

@T.J.Crowder Я попробую, но это немного сложно, так как это расширение для браузера Google.

simonzack 15.06.2019 10:33
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
1
1 019
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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>

Как насчет совпадающих элементов-потомков добавленных элементов? :-)

T.J. Crowder 15.06.2019 10:45

Хм, может быть, я не совсем ясно объяснил, но цель состоит в том, чтобы иметь функцию, которая проксирует querySelectorAll, я уже пытался использовать filter, текущее жестко запрограммированное решение использует генератор.

simonzack 15.06.2019 10:45

@simonzack querySelectorAll работает только с документом или элементом — он не работает с NodeList, поэтому вы не можете использовать его напрямую — что-то вроде .filter и .matches — лучшая альтернатива.

CertainPerformance 15.06.2019 10:49

@simonzack - Хитрость в том, что, поскольку обратный вызов наблюдателя получает массив записей, которые затем имеют NodeLists добавленных узлов, вам в основном нужно добавить querySelectorAll к Array.prototype, что, вероятно, не было бы идеальным. :-)

T.J. Crowder 15.06.2019 10:54
Ответ принят как подходящий

Я уверен, вы знаете, что ваш обратный вызов получает массив 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 15.06.2019 10:49

@simonzack - Да, matches это здорово. :-)

T.J. Crowder 15.06.2019 10:51

Другие вопросы по теме