Определить, когда узел удален (или удален из DOM, потому что родитель был)

Я хочу определить, когда узел (скажем, nodeX) больше не доступен, либо потому, что он был удален, либо потому, что его родитель (или родительский родитель) был удален.

Пока что все, о чем я могу думать, это использовать Mutation Observer, чтобы увидеть любые удаления на странице, и проверить, был ли удаленный узел nodeX или имел узел X для потомка.

Есть ли более простой способ?


Обратите внимание: насколько я понимаю, связанный вопрос (что этот вопрос "является дубликатом") спрашивает "как я могу обнаружить [прямое] удаление узла". Мой спрашивает: "Как я могу обнаружить удаление узла или его родителя (или любого другого предка)".

Насколько я понимаю, с наблюдателями мутаций это не так просто: вам нужно проверить каждый удаленный узел, чтобы узнать, был ли он предком.

Это то, что я пытаюсь подтвердить или опровергнуть.

Насколько я понимаю, это отличается от связанного вопроса.

3 ответа

Решение

Вот реализация, которая идентифицирует, как элемент был удален (напрямую или потому, что родитель был удален)

var target = document.querySelector('#to-be-removed');

var observer = new MutationObserver(function(mutations) {
  // check for removed target
  mutations.forEach(function(mutation) {
    var nodes = Array.from(mutation.removedNodes);
    var directMatch = nodes.indexOf(target) > -1
    var parentMatch = nodes.some(parent => parent.contains(target));
    if (directMatch) {
      console.log('node', target, 'was directly removed!');
    } else if (parentMatch) {
      console.log('node', target, 'was removed through a removed parent!');
    }

  });
});

var config = {
  subtree: true,
  childList: true
};
observer.observe(document.body, config);


var qs = document.querySelector.bind(document);
qs('#ul').addEventListener('click', function(){qs('ul').remove();}, false)
qs('#li').addEventListener('click', function(){qs('#to-be-removed').remove();}, false)
<ul>
  <li>list item 1</li>
  <li>list item 2</li>
  <li id="to-be-removed">list item 3</li>
  <li>list item 4</li>
</ul>

<button id="ul">remove ul</button>
<button id="li">remove li</button>

Принятый ответ потерпит неудачу, если удаленное поддерево будет видоизменено после удаления из документа. Например:

target.parent.remove();
target.remove();

сгенерирует один вызов для наблюдателя мутации для удаления родительского узла (удаление целевого узла не будет сообщено наблюдателю, как это произошло, когда поддерево уже было удалено из документа).

var parentMatch = nodes.some(parent => parent.contains(target));

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

По этой причине, столкнувшись с такой же проблемой, как вопросник, я создал WeakSet предков целевого узла. Используя наблюдателя мутаций, прикрепленного к корню документа, я сравнил мутации с этим набором и целью. Если событие удаления узла мутации включает узел в этом наборе или целевой узел, я знаю, что целевой узел был удален из дерева. Это не означает, что узел все еще удален (возможно, он был добавлен обратно) или что узел все еще является потомком предков в моем наборе. Но я могу быть уверен, что узел был удален в прошлом.

Вы должны быть осторожны, чтобы различать состояние DOM в момент после мутации и состояние в момент получения события мутации.

Это было задано ранее при переполнении стека. Как обнаружить добавление / удаление элемента из элемента dom?

Если вы просто хотите проверить, существует ли что-то в определенный момент времени, вы, очевидно, можете сделать что-то вроде:

if (!document.querySelector(".nonexistent")) {
  console.log("doesn't exist");
}

В противном случае наблюдатели мутаций - ваш единственный выбор.

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