我怎样才能确定一个dynamic创build的DOM元素是否已经添加到DOM?

根据规范 ,只有BODYFRAMESET元素提供了一个“onload”事件来附加,但我想知道什么时候dynamic创build的DOM元素已经被添加到DOM中的JavaScript。

我目前使用的超天真的启发式,其工作如下:

  • 遍历元素的parentNode属性,直到find最终的祖先(即parentNode.parentNode.parentNode.etc,直到parentNode为null)
  • 如果最终的祖先有一个定义的,非null的身体属性
    • 假设有问题的元素是dom的一部分
  • 其他
    • 在100毫秒内再次重复这些步骤

我所追求的是要么证实我所做的事情是足够的(再次,它在IE7和FF3中都有效),要么是更好的解决scheme,无论出于何种原因,我都完全忘记了; 也许其他属性我应该检查,等等


编辑:我想要一个浏览器不可知的方式做到这一点,我不住在一个浏览器的世界,不幸的是, 说,浏览器的具体信息是赞赏,但请注意哪个浏览器,你知道它的工作。谢谢!

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

 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事件的方法。 这是这个函数(尽pipe我使用的是稍微不同的,因为我和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 

希望对某人有用。

注意:我最终使用这个解决scheme而不是Darryl的答案是因为getElementById技术只适用于你在同一个文档中; 我在页面上有一些iframe,页面之间以一些复杂的方式进行通信 – 当我尝试这样做时,问题是无法find元素,因为它是不同于它正在执行的代码的一部分。

最直接的答案是利用由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,那么下面是另一个使用animation来检测节点插入的巧妙技巧: http : //www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/

你能不能做一个document.getElementById('newElementId'); 看看是否回报真实。 如果没有,像你说的,等待100ms,然后再试一次?

而不是走DOM文档元素,只需使用element.ownerDocument 。 请参阅https://developer.mozilla.org/en-US/docs/DOM/Node.ownerDocument并执行此操作:;

 element.ownerDocument.body.contains(element) 

你很好。

您可以查询document.getElementsByTagName(“*”)。length或创build一个自定义的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对Ajaxian上的Peppy库有了一个评论 ,似乎表明他的Sizzle库可能能够处理DOM插入事件。 我很好奇,看看是否会有代码来处理IE

在轮询的概念之后,我读了一些元素属性,直到元素被附加到文档(例如element.scrollTop)才可用,也许你可以轮询,而不是做所有的DOM遍历。

最后一件事情:在IE中,可能值得探索的一种方法是使用onpropertychange事件。 我认为追加到文件是必然触发该事件至less有一个属性。

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

我已经看到了通过比较document.body.innerHTML到上次保存的.innerHTML来监视更改的DOM更改实现​​,它不完全是优雅的(但工作)。 既然你最终要检查是否已经添加了一个特定的节点,那么你最好检查每个中断。

我能想到的改进是 使用.offsetParent而不是.parentNode因为它可能会削减您的循环中的一些父母 (看评论)。 或者使用compareDocumentIndex() IE外的所有compareDocumentIndex() ,并且testing.sourceIndex对IE执行(如果节点不在DOM中,则应为-1)。

这也可能有助于: John Resig的X浏览器compareDocumentIndex实现 。

你想要DOMNodeInserted事件(或DOMNodeInsertedIntoDocument )。

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

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

函数isDisconnected(节点){
    返回!节点||  !node.parentNode ||  node.parentNode.nodeType === 11;
 }

nodeType === 11定义了DOCUMENT_FRAGMENT_NODE,这是一个没有连接到文档对象的节点。

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

 var finalElement=null; 

我在循环中构builddom对象,当循环处于最后一个循环时,创buildlastDomObject

  finalElement=lastDomObject; 

离开循环:

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

下一个动作可以使用任何新创build的dom对象。 它在第一次尝试。

IE中不支持DOMNodeInsertedDOMNodeInsertedIntoDocument事件。 另一个从DOM的IE [only]特性中缺less的是compareDocumentPosition函数,它被devise用来做它的名字。

至于build议的解决scheme,我认为没有更好的(除非你做一些肮脏的窍门,如覆盖原生DOM成员的实现)

此外,更正问题的标题:dynamic创build的元素是DOM创build之时的一部分,可以附加到文档片段或其他元素,但可能不会附加到文档中。

我也认为你所描述的问题不是你有的问题,这个问题看起来是你潜在的解决scheme之一。 你能详细解释一下用例吗?