如何确定是否已将动态创建的DOM元素添加到DOM中?

时间:2008-10-20 22:38:38

标签: javascript dom

According to spec,只有BODYFRAMESET元素提供附加的“onload”事件,但我想知道何时添加了动态创建的DOM元素JavaScript中的DOM。

我目前正在使用的超天真的启发式方法如下:

  • 遍历元素的 parentNode 属性,直到找到最终祖先(即parentNode.parentNode.parentNode.etc,直到parentNode为空)为

  • 如果最终祖先具有已定义的非空正文属性

    • 假设有问题的元素是dom的一部分
  • 否则

    • 在100毫秒内再次重复这些步骤

我所追求的是确认我正在做的事情是足够的(再次,它在IE7和FF3中工作)或者是一个更好的解决方案,无论出于何种原因,我完全忘记了;也许我应该检查的其他属性等等。


编辑:我想要一种与浏览器无关的方式来实现这一点,不幸的是,我没有生活在一个浏览器的世界中;也就是说,浏览器特定的信息很受欢迎,但请注意您知道 工作的浏览器。谢谢!

11 个答案:

答案 0 :(得分:27)

更新:对于任何对它感兴趣的人,这是我最终使用的实现:

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;
}

我想要的原因是提供一种合成DOM元素的onload事件的方法。这是该函数(虽然我使用的东西略有不同,因为我将它与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's answer的原因是因为getElementById技术仅在您位于同一文档中时才有效;我在页面上有一些iframe,页面之间以某些复杂的方式进行通信 - 当我尝试这个时,问题是它无法找到元素,因为它是一个不同的文档的一部分而不是它在执行的代码

答案 1 :(得分:20)

最直接的答案是使用Chrome,Firefox(Gecko),Internet Explorer,Opera和Safari支持的Node.contains方法。这是一个例子:

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/

答案 2 :(得分:8)

你能否做document.getElementById('newElementId');并看看是否返回true。如果没有,就像你说的那样,等待100ms然后再试一次?

答案 3 :(得分:8)

不要将DOM树移到文档元素,而是使用element.ownerDocument。 见这里:https://developer.mozilla.org/en-US/docs/DOM/Node.ownerDocument 并这样做:

element.ownerDocument.body.contains(element)

你很好。

答案 4 :(得分:2)

您可以查询 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";
});

<强>更新

根据请求,将我的评论中的信息添加到我的帖子中

今天Ajaxian上的Peppy库上有一个comment by John Resig似乎表明他的Sizzle库可能能够处理DOM插入事件。我很想知道是否会有代码来处理IE

关于轮询的想法,我已经读过在元素被附加到文档之前一些元素属性不可用(例如element.scrollTop),也许你可以轮询它而不是做所有的DOM遍历

最后一件事:在IE中,一种可能值得探索的方法是使用onpropertychange事件。我认为将其附加到文档必然会触发至少一个属性的事件。

答案 5 :(得分:2)

在一个完美的世界中,你可以勾选突变事件。我怀疑他们即使在标准浏览器上也能可靠地工作。听起来你已经实现了一个变异事件,所以你可以添加它来使用本机变异事件,而不是在支持它们的浏览器上进行超时轮询。

我见过DOM-change实现,它通过比较document.body.innerHTML与上次保存的.innerHTML来监控变化,这不是很优雅(但是很有效)。由于您最终要检查是否已添加特定节点,因此您最好只检查每个中断。

我能想到的改进是使用.offsetParent而不是.parentNode因为它可能会从你的循环中删除一些父母(参见评论)。或者在除IE之外的所有IE上使用compareDocumentIndex()并测试IE的.sourceIndex属性(如果节点不在DOM中,则应为-1)。

这也可能会有所帮助:X browser compareDocumentIndex implementaion by John Resig

答案 6 :(得分:1)

您想要DOMNodeInserted事件(或DOMNodeInsertedIntoDocument)。

编辑:完全有可能IE不支持这些事件,但我现在无法测试。

答案 7 :(得分:0)

这是从jQuery代码中提取的问题的另一种解决方案。 以下函数可用于检查节点是否连接到DOM或是否“断开连接”。

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

nodeType === 11定义DOCUMENT_FRAGMENT_NODE,它是未连接到文档对象的节点。

有关详细信息,请参阅John Resig的以下文章: http://ejohn.org/blog/dom-documentfragments/

答案 8 :(得分:0)

var finalElement=null; 

我在循环中构建dom对象,当循环处于最后一个循环并且创建lastDomObject时:

 finalElement=lastDomObject; 

离开循环:

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

下一个操作可以使用任何新创建的dom对象。它是第一次尝试。

答案 9 :(得分:0)

您应该使用MutationObserver来检测元素何时添加到DOM。 MutationObservers现在在所有现代浏览器中得到广泛支持(Chrome 26 +,Firefox 14+ ,IE11,Edge,Opera 15+等)。

这是一个简单的示例,说明如何在将元素添加到DOM时使用MutationObserver进行侦听。

为简洁起见,我使用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

您无需遍历mutations中存储的每个MutationRecord,因为您可以直接在document.contains执行myElement检查。

要提高效果,请将document替换为DOM中包含myElement的特定元素。

答案 10 :(得分:-1)

IE中不支持 DOMNodeInserted DOMNodeInsertedIntoDocument 事件。 DOM [IE]功能的另一个缺失是 compareDocumentPosition 功能,其功能与其名称相同。

至于建议的解决方案,我认为没有更好的解决方案(除非你做了一些肮脏的技巧,比如覆盖原生的DOM成员实现)

此外,更正问题的标题:动态创建的元素从创建时起就是DOM的一部分,它可以附加到文档片段或其他元素,但它可能不会附加到文档

我还认为你所描述的问题不是你所遇到的问题,这个问题对你来说是一个潜在的解决方案。您可以详细解释用例吗?