41

仕様によるとBODY、およびのみがFRAMESETアタッチする「onload」イベントを提供しますが、動的に作成された DOM 要素が JavaScript の DOM にいつ追加されたかを知りたいです。

私が現在使用している非常に単純なヒューリスティックは次のとおりです。

  • 最終的な祖先が見つかるまで、要素のparentNodeプロパティをトラバースします(つまり、parentNodeがnullになるまでparentNode.parentNode.parentNode.etc)

  • 究極の祖先が定義済みの null 以外のbodyプロパティを持っている場合

    • 問題の要素が dom の一部であると仮定します
  • そうしないと

    • これらの手順を 100 ミリ秒でもう一度繰り返します

私が求めているのは、私がやっていることで十分であることの確認 (IE7 と FF3 の両方で動作しています) か、何らかの理由で完全に忘れていたより良い解決策です。おそらく、チェックすべき他のプロパティなどです。


編集:これを行うにはブラウザーに依存しない方法が必要です。残念ながら、私は1つのブラウザーの世界に住んでいません。とはいえ、ブラウザ固有の情報はありがたいですが、それが動作することを知っているブラウザに注意してください。ありがとう!

4

11 に答える 11

30

更新: 興味のある人のために、私が最終的に使用した実装を次に示します。

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 手法が機能するためです。ページにいくつかのiframeがあり、ページは複雑な方法で相互に通信します-これを試したとき、実行中のコードとは異なるドキュメントの一部であるため、要素が見つからないという問題がありました.

于 2009-05-12T03:10:34.650 に答える
21

最も簡単な答えは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/

于 2012-12-05T14:57:11.840 に答える
8

いいえ、document.getElementById('newElementId');それが true を返すかどうかを確認できますか。そうでない場合は、あなたが言うように、100ミリ秒待ってからもう一度試してください。

于 2008-10-20T22:53:14.713 に答える
2

完璧な世界では、突然変異イベントをフックすることができます。標準のブラウザでも確実に動作するかどうか疑問です。すでにミューテーションイベントを実装しているようですので、これを追加して、ネイティブミューテーションイベントをサポートするブラウザでタイムアウトポーリングを使用するのではなく、ネイティブミューテーションイベントを使用することができます。

document.body.innerHTML最後に保存されたものと比較して変更を監視するDOM変更の実装を見てきました.innerHTMLが、これは正確にはエレガントではありません(ただし機能します)。最終的には特定のノードがまだ追加されているかどうかを確認するので、各割り込みを確認する方がよいでしょう。

私が考えることができる改善はループから数人の親をカットする可能性が高いためでは.offsetParentなく、を使用する.parentNode(コメントを参照)。またはcompareDocumentIndex()、IEを除くすべてで使用し、IEの適切なテストを行います.sourceIndex(ノードがDOMにない場合は-1にする必要があります)。

これも役立つかもしれません:JohnResigによるXブラウザcompareDocumentIndexの実装

于 2008-10-21T16:07:30.367 に答える
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 ライブラリに関するJohn Resig によるコメントがありました。これは、彼の Sizzle ライブラリが DOM 挿入イベントを処理できる可能性があることを示唆しているように思われます。IEも処理するコードがあるかどうか知りたいです

ポーリングの考え方に従って、要素がドキュメントに追加されるまで (たとえば element.scrollTop など) いくつかの要素のプロパティは使用できないことを読みました。

最後に 1 つ: IE で検討する価値のあるアプローチの 1 つは、onpropertychange イベントを使用することです。ドキュメントに追加すると、少なくとも1つのプロパティに対してそのイベントがトリガーされると思います。

于 2008-10-21T05:51:42.403 に答える
1

DOMNodeInsertedイベント (または)が必要ですDOMNodeInsertedIntoDocument

編集: これらのイベントが IE でサポートされていない可能性は十分にありますが、現時点ではテストできません。

于 2008-10-20T22:42:01.147 に答える
0

これは、jQuery コードから抽出された問題に対する別の解決策です。次の関数を使用して、ノードが DOM に接続されているか、または「切断」されているかを確認できます。

関数 isDisconnected( ノード ) {
    ! ノードを返します || !ノード.親ノード || node.parentNode.nodeType === 11;
}

nodeType === 11 は、ドキュメント オブジェクトに接続されていないノードである DOCUMENT_FRAGMENT_NODE を定義します。

詳細については、John Resig による次の記事を参照してください: http://ejohn.org/blog/dom-documentfragments/

于 2010-03-10T00:29:33.520 に答える
0
var finalElement=null; 

lastDomObjectループが最後のサイクルにあり、次に作成されるときに、ループ内で dom オブジェクトを構築しています。

 finalElement=lastDomObject; 

ループを離れる:

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

次のアクションでは、新しく作成された任意の dom オブジェクトを使用できます。最初の試行で機能しました。

于 2013-04-11T00:28:08.360 に答える
-1

IEでは、DOMNodeInsertedイベントとDOMNodeInsertedIntoDocumentイベントのどちらもサポートされていません。DOMのIE[のみ]機能に欠けているもう1つの機能は、その名前が示すことを実行するように設計されたcompareDocumentPosition関数です。

提案された解決策に関しては、これ以上の解決策はないと思います(ネイティブDOMメンバーの実装を上書きするなどの汚いトリックを行わない限り)

また、質問のタイトルを修正します。動的に作成された要素は、作成された瞬間からDOMの一部であり、ドキュメントフラグメントまたは別の要素に追加できますが、ドキュメントに追加されない場合があります。

また、あなたが説明した問題はあなたが抱えている問題ではないと思います。これはあなたの問題に対する潜在的な解決策の1つであるように見えます。ユースケースについて詳しく説明していただけますか?

于 2008-10-20T23:03:28.780 に答える