Как определить, был ли добавлен динамически созданный элемент DOM в DOM?
В соответствии с spec только элементы BODY
и FRAMESET
предоставляют событие "onload" для attach, но я хотел бы знать, когда динамически созданный элемент DOM был добавлен в DOM в JavaScript.
Супер-наивные эвристики, которые я сейчас использую, которые работают, следующие:
- Переместите свойство parentNode элемента обратно до тех пор, пока я не найду конечного предка (то есть parentNode.parentNode.parentNode.etc, пока parentNode не будет null)
- Если у конечного предка есть определенное, ненулевое свойство тело
- Предположим, что этот элемент является частью dom
- else
- повторите эти шаги за 100 миллисекунд
То, что я получаю, является либо подтверждением того, что то, что я делаю, достаточно (опять же, оно работает как в IE7, так и в FF3) или в лучшем решении, которое по какой-то причине я совершенно не обращал на это внимания; возможно, другие свойства, которые я должен проверять и т.д.
ИЗМЕНИТЬ: Я хочу, чтобы браузер был несовместим с этим, я не живу в мире с одним браузером, к сожалению; это говорит о том, что информация, относящаяся к браузеру, оценивается, но учтите, какой браузер вы знаете, что он работает. Спасибо!
Ответы
Ответ 1
UPDATE: для всех, кто интересуется этим, вот реализация, которую я наконец использовал:
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 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
Надеюсь, что это полезно кому-то.
ПРИМЕЧАНИЕ. Причина, по которой я закончил это решение, а не Darryl answer, состояла в том, что метод getElementById работает только в том случае, если вы находитесь в пределах тот же документ; У меня есть некоторые iframes на странице, и страницы обмениваются друг с другом сложными способами - когда я это пробовал, проблема заключалась в том, что он не мог найти элемент, потому что он был частью другого документа, кроме кода, который он выполнял в.
Ответ 2
Самый простой ответ - использовать метод 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 на свою страницу, вот еще один умный метод, который использует анимации для обнаружения вложений node: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/
Ответ 3
Не можете ли вы сделать document.getElementById('newElementId');
и посмотреть, возвращает ли это значение true. Если нет, как вы говорите, подождите 100 мс и повторите попытку?
Ответ 4
Вместо того, чтобы ходить по дереву DOM до элемента документа, просто используйте element.ownerDocument
.
см. здесь: https://developer.mozilla.org/en-US/docs/DOM/Node.ownerDocument
и сделайте следующее:
element.ownerDocument.body.contains(element)
и вы хорошо.
Ответ 5
Вы можете запросить 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";
});
Обновление
По запросу добавление информации из моих комментариев в мой пост
Был комментарий John Resig в библиотеке Peppy на Ajaxian сегодня, который, казалось, предполагал, что его библиотека Sizzle может обрабатывать События вставки DOM. Мне любопытно узнать, будет ли код для обработки IE также
Следуя идее опроса, я прочитал, что некоторые свойства элемента недоступны до тех пор, пока элемент не будет добавлен к документу (например, element.scrollTop), возможно, вы могли бы опросить это, а не делать все обход DOM.
Последнее: в IE один подход, который стоит изучить, - это играть с событием onpropertychange. Я полагаю, что добавление его к документу должно инициировать это событие для хотя бы одного свойства.
Ответ 6
В идеальном мире вы можете подключить мутационные события. У меня есть сомнения, что они работают надежно даже в браузерах стандартов. Похоже, что вы уже внедрили событие с мутацией, поэтому вы можете добавить это, чтобы использовать собственное мутационное событие, а не тайм-аут опроса в браузерах, которые их поддерживают.
Я видел реализации DOM-изменений, которые отслеживают изменения, сравнивая document.body.innerHTML
с последним сохраненным .innerHTML
, который не совсем изящный (но работает). Поскольку вы в конечном счете собираетесь проверить, добавлен ли какой-либо конкретный node, тогда вам лучше просто проверить, что каждое прерывание.
Усовершенствования, о которых я могу думать, - это , используя .offsetParent
, а не .parentNode
, поскольку он, скорее всего, вырезает несколько родителей из вашего цикла (см. комментарии). Или используя compareDocumentIndex()
для всех, кроме IE, и для тестирования .sourceIndex
для IE (должно быть -1, если node не находится в DOM).
Это также может помочь: X-браузер compareDocumentIndex, реализуемый Джоном Ресигом.
Ответ 7
Вы хотите событие DOMNodeInserted
(или DOMNodeInsertedIntoDocument
).
Изменить: вполне возможно, что эти события не поддерживаются IE, но я не могу проверить это прямо сейчас.
Ответ 8
Вот еще одно решение проблемы, извлеченное из кода jQuery.
Следующая функция может использоваться, чтобы проверить, подключен ли node к DOM или если он "отключен".
function isDisconnected( node ) {
return !node || !node.parentNode || node.parentNode.nodeType === 11;
}
A nodeType === 11 определяет DOCUMENT_FRAGMENT_NODE, который является node, не связанным с объектом документа.
Для получения дополнительной информации см. следующую статью Джона Ресига:
http://ejohn.org/blog/dom-documentfragments/
Ответ 9
var finalElement=null;
Я строю dom-объекты в цикле, когда цикл находится в последнем цикле, и создается lastDomObject
, затем:
finalElement=lastDomObject;
оставить цикл:
while (!finalElement) { } //this is the delay...
Следующее действие может использовать любой из вновь созданных объектов dom. Он работал с первой попытки.
Ответ 10
В IE не поддерживается ни один из событий DOMNodeInserted или DOMNodeInsertedIntoDocument. Еще одна недостающая функция IE [только] DOM - это функция compareDocumentPosition, которая предназначена для того, чтобы делать то, что предлагает ее название.
Что касается предлагаемого решения, я думаю, что нет лучшего (если вы не совершаете какие-то грязные трюки, например, перезаписываете реализации собственных DOM-элементов)
Кроме того, исправление заголовка вопроса: динамически созданный элемент является частью DOM с момента его создания, его можно добавить к фрагменту документа или к другому элементу, но он не может быть добавлен к документу.
Я также считаю, что проблема, о которой вы описали, не является той проблемой, которая у вас есть, это выглядит скорее одним из потенциальных решений вашей проблемы. Можете ли вы, вероятно, подробно объяснить прецедент?