Как я могу определить, был ли динамически созданный элемент DOM добавлен в DOM? - PullRequest
38 голосов
/ 21 октября 2008

Согласно спецификации , только элементы BODY и FRAMESET предоставляют событие «onload» для подключения, но я хотел бы знать, когда создается динамически Элемент DOM был добавлен в DOM в JavaScript.

Используемые мной в настоящее время сверхъестественные эвристики, которые работают следующим образом:

  • Обходите parentNode свойство элемента назад, пока я не найду конечного предка (то есть parentNode.parentNode.parentNode.etc, пока parentNode не станет нулевым)

  • Если конечный предок имеет определенное ненулевое тело свойство

    • предположим, что данный элемент является частью dom
  • иначе

    • повторите эти шаги снова через 100 миллисекунд

Что мне нужно, так это либо подтверждение того, что я делаю, достаточно (опять-таки, это работает как в IE7, так и в FF3), либо лучшее решение, которое по какой-то причине я полностью забыл; возможно другие свойства, которые я должен проверить, и т. д.


РЕДАКТИРОВАТЬ: я хочу, чтобы это не зависело от браузера, к сожалению, я не живу в мире с одним браузером; Тем не менее, информация, относящаяся к конкретному браузеру, приветствуется, но, пожалуйста, обратите внимание, в каком браузере вы знаете, что она работает . Спасибо!

Ответы [ 11 ]

27 голосов
/ 12 мая 2009

ОБНОВЛЕНИЕ: для тех, кто заинтересован в этом, вот реализация, которую я наконец использовал:

function isInDOMTree(node) {
   // If the farthest-back ancestor of our node has a "body"
   // property (that node would be the document itself), 
   // we assume it is in the page's DOM tree.
   return !!(findUltimateAncestor(node).body);
}
function findUltimateAncestor(node) {
   // Walk up the DOM tree until we are at the top (parentNode 
   // will return null at that point).
   // NOTE: this will return the same node that was passed in 
   // if it has no ancestors.
   var ancestor = node;
   while(ancestor.parentNode) {
      ancestor = ancestor.parentNode;
   }
   return ancestor;
}

Причина, по которой я этого хотел, - предоставить способ синтеза события onload для элементов DOM. Вот эта функция (хотя я использую что-то немного другое, потому что я использую это в сочетании с MochiKit ):

function executeOnLoad(node, func) {
   // This function will check, every tenth of a second, to see if 
   // our element is a part of the DOM tree - as soon as we know 
   // that it is, we execute the provided function.
   if(isInDOMTree(node)) {
      func();
   } else {
      setTimeout(function() { executeOnLoad(node, func); }, 100);
   }
}

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

var mySpan = document.createElement("span");
mySpan.innerHTML = "Hello world!";
executeOnLoad(mySpan, function(node) { 
   alert('Added to DOM tree. ' + node.innerHTML);
});

// now, at some point later in code, this
// node would be appended to the document
document.body.appendChild(mySpan);

// sometime after this is executed, but no more than 100 ms after,
// the anonymous function I passed to executeOnLoad() would execute

Надеюсь, это кому-нибудь пригодится.

ПРИМЕЧАНИЕ: причина, по которой я выбрал это решение, а не ответ Даррила , заключалась в том, что метод getElementById работает только в том случае, если вы находитесь в одном и том же документе; У меня есть несколько фреймов на странице, и страницы взаимодействуют друг с другом сложным образом - когда я попробовал это, проблема заключалась в том, что он не мог найти элемент, потому что он был частью документа, отличного от кода, который он выполнял в .

20 голосов
/ 05 декабря 2012

Самый простой ответ - использовать метод Node.contains, поддерживаемый Chrome, Firefox (Gecko), Internet Explorer, Opera и Safari. Вот пример:

var el = document.createElement("div");
console.log(document.body.contains(el)); // false
document.body.appendChild(el);
console.log(document.body.contains(el)); // true
document.body.removeChild(el);
console.log(document.body.contains(el)); // false

В идеале мы бы использовали document.contains(el), но в IE это не работает, поэтому мы используем document.body.contains(el).

К сожалению, вам все еще нужно опросить, но проверить, есть ли элемент в документе, все же очень просто:

setTimeout(function test() {
  if (document.body.contains(node)) {
    func();
  } else {
    setTimeout(test, 50);
  }
}, 50);

Если у вас все в порядке с добавлением CSS на вашу страницу, вот еще один умный метод, который использует анимацию для обнаружения вставки узла: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/

8 голосов
/ 06 апреля 2013

Вместо перехода по дереву DOM к элементу документа просто используйте element.ownerDocument. смотри здесь: https://developer.mozilla.org/en-US/docs/DOM/Node.ownerDocument и сделайте это:

element.ownerDocument.body.contains(element)

и ты в порядке.

8 голосов
/ 21 октября 2008

Не можете ли вы сделать document.getElementById('newElementId'); и посмотреть, вернет ли это значение true. Если нет, как вы говорите, подождите 100 мс и повторите попытку?

2 голосов
/ 21 октября 2008

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

Я видел реализации DOM-изменений, которые отслеживают изменения, сравнивая document.body.innerHTML с последним сохраненным .innerHTML, что не совсем элегантно (но работает). Поскольку в конечном итоге вы собираетесь проверить, был ли добавлен конкретный узел, лучше проверить каждое прерывание.

Улучшения, о которых я могу подумать, включают с использованием .offsetParent вместо .parentNode, так как это, вероятно, исключит из вашего круга нескольких родителей (см. Комментарии). Или используя compareDocumentIndex() на всех, кроме IE, и проверяйте правильность .sourceIndex для IE (должно быть -1, если узел не находится в DOM).

Это также может помочь: Реализация X-браузера сравнениемDocumentIndex от Джона Резига .

2 голосов
/ 21 октября 2008

Вы можете запросить document.getElementsByTagName ("*"). Length или создать собственную функцию appendChild, например:

var append = function(parent, child, onAppend) {
  parent.appendChild(child);
  if (onAppend) onAppend(child);
}

//inserts a div into body and adds the class "created" upon insertion
append(document.body, document.createElement("div"), function(el) {
  el.className = "created";
});

Обновление

По запросу, добавив информацию из моих комментариев в мой пост

Был комментарий Джона Резига о библиотеке Peppy на Ajaxian сегодня, который, казалось, предполагал, что его библиотека Sizzle могла бы обрабатывать события вставки DOM. Мне любопытно посмотреть, будет ли также код для обработки IE

Следуя идее опроса, я прочитал, что некоторые свойства элемента недоступны, пока элемент не добавлен в документ (например, element.scrollTop), возможно, вы могли бы опрашивать его, вместо того чтобы выполнять весь обход DOM. .

И последнее: в IE один подход, который стоит изучить, - поиграть с событием onpropertychange. Я считаю, что добавление его в документ обязательно должно вызвать это событие хотя бы для одного свойства.

1 голос
/ 21 октября 2008

Требуется событие DOMNodeInserted (или DOMNodeInsertedIntoDocument).

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

0 голосов
/ 30 января 2018

A MutationObserver - это то, что вы должны использовать для определения момента добавления элемента в DOM. MutationObservers теперь широко поддерживаются во всех современных браузерах (Chrome 26+, Firefox 14 +, IE11, Edge, Opera 15+ и т. Д.).

Вот простой пример того, как вы можете использовать MutationObserver для прослушивания, когда элемент добавляется в DOM.

Для краткости я использую синтаксис jQuery для построения узла и вставки его в DOM.

var myElement = $("<div>hello world</div>")[0];

var observer = new MutationObserver(function(mutations) {
   if (document.contains(myElement)) {
        console.log("It's in the DOM!");
        observer.disconnect();
    }
});

observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});

$("body").append(myElement); // console.log: It's in the DOM!

Обработчик события observer сработает всякий раз, когда какой-либо узел добавляется или удаляется из document. Внутри обработчика мы затем выполняем проверку contains, чтобы определить, находится ли myElement в document.

Вам не нужно перебирать все записи MutationRecord, хранящиеся в mutations, потому что вы можете выполнить проверку document.contains непосредственно на myElement.

Чтобы повысить производительность, замените document конкретным элементом, который будет содержать myElement в DOM.

0 голосов
/ 11 апреля 2013
var finalElement=null; 

Я строю объекты dom в цикле, когда цикл находится в последнем цикле и затем создается lastDomObject:

 finalElement=lastDomObject; 

выход из цикла:

while (!finalElement) { } //this is the delay... 

Следующее действие может использовать любой из вновь созданных объектов DOM. Сработало с первой попытки.

0 голосов
/ 10 марта 2010

Вот еще одно решение проблемы, извлеченное из кода jQuery. Следующая функция может использоваться для проверки, подключен ли узел к DOM или «отключен».

function isDisconnected( node ) {
    return !node || !node.parentNode || node.parentNode.nodeType === 11;
}

nodeType === 11 определяет DOCUMENT_FRAGMENT_NODE, который является узлом, не связанным с объектом документа.

Для получения дополнительной информации см. Следующую статью Джона Резига: http://ejohn.org/blog/dom-documentfragments/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...