簡體   English   中英

如何確定是否已將動態創建的DOM元素添加到DOM中?

[英]How can I determine if a dynamically-created DOM element has been added to the DOM?

根據規范 ,只有BODYFRAMESET元素提供附加的“onload”事件,但我想知道在JavaScript中將動態創建的DOM元素添加到DOM中的時間。

我目前正在使用的超級天真的啟發式方法如下:

  • 遍歷元素的parentNode屬性,直到找到最終的祖先(即parentNode.parentNode.parentNode.etc,直到parentNode為null)

  • 如果最終祖先具有已定義的非空屬性

    • 假設有問題的元素是dom的一部分
  • 其他

    • 在100毫秒內再次重復這些步驟

我所追求的是確認我正在做的事情是足夠的(再次,它在IE7和FF3中工作)或更好的解決方案,無論出於何種原因,我完全忘記了; 也許我應該檢查其他屬性等。


編輯:我想要一個與瀏覽器無關的方法,不幸的是,我並不住在一個瀏覽器的世界里; 也就是說,瀏覽器特定的信息是值得贊賞的,但是請注意哪個瀏覽器知道它確實有效。謝謝!

更新:對於任何對它感興趣的人,這是我最終使用的實現:

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的答案的原因是因為getElementById技術只有在同一個文檔中才有效。 我在頁面上有一些iframe,頁面之間以某些復雜的方式進行通信 - 當我嘗試這個時,問題是它無法找到元素,因為它是一個不同的文檔的一部分而不是它執行的代碼。

最直接的答案是使用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/

而不是將DOM樹移動到文檔元素,只需使用element.ownerDocument 請參閱此處: https//developer.mozilla.org/en-US/docs/DOM/Node.ownerDocument並執行以下操作:

element.ownerDocument.body.contains(element)

而且你很好。

你能不做一個document.getElementById('newElementId'); 並查看是否返回true。 如果沒有,就像你說的那樣,等待100毫秒再試一次?

您可以查詢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今天在Ajaxian的Peppy庫上發表評論似乎表明他的Sizzle庫可能能夠處理DOM插入事件。 我很想知道是否會有代碼來處理IE

根據輪詢的想法,我已經讀過在元素被附加到文檔之前一些元素屬性不可用(例如element.scrollTop),也許你可以輪詢它而不是進行所有的DOM遍歷。

最后一件事:在IE中,一種可能值得探索的方法是使用onpropertychange事件。 我估計將其附加到文檔必然會觸發至少一個屬性的事件。

在完美的世界中,您可以勾選突變事件。 我懷疑他們即使在標准瀏覽器上也能可靠地工作。 聽起來你已經實現了一個變異事件,所以你可以添加它來使用本機變異事件,而不是在支持它們的瀏覽器上進行超時輪詢。

我已經看到DOM-change實現通過將document.body.innerHTML與上次保存的.innerHTML進行比較來監視更改,這不是很優雅(但是很有效)。 由於您最終將檢查是否已添加特定節點,因此您最好只檢查每個中斷。

我能想到的改進是 使用.offsetParent而不是.parentNode因為它可能會將一些父.parentNode從循環中刪除 (看評論)。 或者在除IE之外的所有IE上使用compareDocumentIndex()並測試IE的.sourceIndex (如果節點不在DOM中,則應為-1)。

這也可能有所幫助: John Resig的X瀏覽器compareDocumentIndex實現

您需要DOMNodeInserted事件(或DOMNodeInsertedIntoDocument )。

編輯:完全有可能IE不支持這些事件,但我現在無法測試。

您應該使用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!

只要在document添加或刪除任何節點, observer事件處理程序就會觸發。 在處理程序內部,我們然后執行contains檢查以確定myElement現在是否在document

您不需要遍歷存儲在mutations每個MutationRecord,因為您可以直接在myElement執行document.contains檢查。

要提高性能,請將document替換為DOM中包含myElement的特定元素。

var finalElement=null; 

我在循環中構建dom對象,當循環在最后一個循環中並且創建lastDomObject

 finalElement=lastDomObject; 

離開循環:

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

下一個操作可以使用任何新創建的dom對象。 它是第一次嘗試。

這是從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/

IE中不支持DOMNodeInsertedDOMNodeInsertedIntoDocument事件。 來自IE [IE]功能的另一個缺點compareDocumentPosition函數,它的設計符合其名稱的建議。

至於建議的解決方案,我認為沒有更好的解決方案(除非你做一些骯臟的技巧,如覆蓋原生DOM成員實現)

此外,更正問題的標題:動態創建的元素從創建時起就是DOM的一部分,它可以附加到文檔片段或另一個元素,但它可能不會附加到文檔。

我還認為你描述的問題不是你所遇到的問題,這個問題對你來說是一個潛在的解決方案。 您可以詳細解釋一下用例嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM