152

DOM要素がページに追加されたときに実行するように選択した関数が必要です。これはブラウザ拡張機能のコンテキストであるため、Webページは私とは独立して実行され、そのソースを変更することはできません。ここでの私のオプションは何ですか?

理論setInterval()的には、要素の存在を継続的に検索し、要素が存在する場合はアクションを実行するために使用できると思いますが、より良いアプローチが必要です。

4

8 に答える 8

93

警告!

この回答は現在古くなっています。DOMレベル4ではMutationObserverが導入され、非推奨のミューテーションイベントを効果的に置き換えることができます。ここに示されているものよりも優れた解決策については、別の質問に対するこの回答を参照してください。真剣に。100ミリ秒ごとにDOMをポーリングしないでください。それはCPUパワーを浪費し、ユーザーはあなたを嫌うでしょう。

ミューテーションイベントは2012年に非推奨になり、挿入された要素は他の誰かのコードによって追加されたため制御できないため、唯一のオプションはそれらを継続的にチェックすることです。

function checkDOMChange()
{
    // check for any new element being inserted here,
    // or a particular node being modified

    // call the function again after 100 milliseconds
    setTimeout( checkDOMChange, 100 );
}

この関数が呼び出されると、100ミリ秒ごとに実行されます。これは、1/10(10分の1)秒です。リアルタイムの要素観察が必要でない限り、それで十分です。

于 2011-09-15T17:30:08.937 に答える
46

実際の答えは「ミューテーションオブザーバーを使用する」(この質問で概説されているように:HTML要素がDOMに動的に追加されているかどうかの判断)ですが、サポート(特にIEで)は制限されています(http://caniuse.com/mutationobserver) 。

したがって、実際の実際の答えは、「ミューテーションオブザーバーを使用する....最終的には。しかし、今のところ、JoseFaetiの答えを使用してください」です:)

于 2013-08-15T16:33:36.770 に答える
20

ミューテーションイベントの廃止との出現の間MutationObserverに、特定の要素がDOMに追加されたときに通知を受ける効率的な方法は、CSS3アニメーションイベントを利用することでした。

ブログ投稿を引用するには:

DOMノード挿入イベントを受信するDOM要素を(CSSセレクターの選択を介して)ターゲットとするCSSキーフレームシーケンスを設定します。私は比較的良性でほとんど使用されていないcssプロパティを使用しました。意図したページスタイルを混乱させないようにoutline-colorを使用しました。コードはかつてclipプロパティを対象としていましたが、バージョン11以降はIEでアニメーション化できなくなりました。とはいえ、アニメーション化できるプロパティはどれでも機能します。好きなものを選択してください。

次に、ノードの挿入を処理するためのデリゲートとして使用するドキュメント全体のanimationstartリスナーを追加しました。アニメーションイベントには、アニメーションを開始したキーフレームシーケンスを示すanimationNameというプロパティがあります。AnimationNameプロパティが、ノード挿入用に追加したキーフレームシーケンス名と同じであることを確認してください。これで準備完了です。

于 2014-06-07T08:11:53.347 に答える
16

ETA 24 Apr 17 これを少しasync/await魔法で単純化したかったのですが、それはもっと簡潔になります:

同じpromisified-observableを使用する:

const startObservable = (domNode) => {
  var targetNode = domNode;

  var observerConfig = {
    attributes: true,
    childList: true,
    characterData: true
  };

  return new Promise((resolve) => {
      var observer = new MutationObserver(function (mutations) {
         // For the sake of...observation...let's output the mutation to console to see how this all works
         mutations.forEach(function (mutation) {
             console.log(mutation.type);
         });
         resolve(mutations)
     });
     observer.observe(targetNode, observerConfig);
   })
} 

呼び出し関数は次のように単純にすることができます。

const waitForMutation = async () => {
    const button = document.querySelector('.some-button')
    if (button !== null) button.click()
    try {
      const results = await startObservable(someDomNode)
      return results
    } catch (err) { 
      console.error(err)
    }
}

タイムアウトを追加したい場合は、ここに示すようPromise.raceな単純なパターンを使用できます。

const waitForMutation = async (timeout = 5000 /*in ms*/) => {
    const button = document.querySelector('.some-button')
    if (button !== null) button.click()
    try {

      const results = await Promise.race([
          startObservable(someDomNode),
          // this will throw after the timeout, skipping 
          // the return & going to the catch block
          new Promise((resolve, reject) => setTimeout(
             reject, 
             timeout, 
             new Error('timed out waiting for mutation')
          )
       ])
      return results
    } catch (err) { 
      console.error(err)
    }
}

オリジナル

ライブラリなしでこれを行うことはできますが、ES6のものを使用する必要があるため、互換性の問題に注意してください(つまり、オーディエンスが主にアーミッシュ、ラッダイト、またはさらに悪いことにIE8ユーザーである場合)

まず、MutationObserverAPIを使用してオブザーバーオブジェクトを作成します。このオブジェクトをpromiseでラップしresolve()、コールバックが発生すると(h / t davidwalshblog)ミューテーションに関するdavid walshブログ記事

const startObservable = (domNode) => {
    var targetNode = domNode;

    var observerConfig = {
        attributes: true,
        childList: true,
        characterData: true
    };

    return new Promise((resolve) => {
        var observer = new MutationObserver(function (mutations) {
            // For the sake of...observation...let's output the mutation to console to see how this all works
            mutations.forEach(function (mutation) {
                console.log(mutation.type);
            });
            resolve(mutations)
        });
        observer.observe(targetNode, observerConfig);
    })
} 

次に、を作成しますgenerator function。これらをまだ使用していない場合は、見逃していることになりますが、簡単な概要は次のとおりです。同期関数のように実行され、yield <Promise>式が見つかると、約束が成立するまで非ブロッキング方式で待機します。達成されました(ジェネレーターはこれ以上のことを行いますが、これが私たちがここで興味を持っていることです)。

// we'll declare our DOM node here, too
let targ = document.querySelector('#domNodeToWatch')

function* getMutation() {
    console.log("Starting")
    var mutations = yield startObservable(targ)
    console.log("done")
}

ジェネレーターのトリッキーな部分は、通常の関数のように「戻らない」ことです。そこで、ヘルパー関数を使用して、通常の関数のようにジェネレーターを使用できるようにします。(繰り返しますが、h / tからdwb

function runGenerator(g) {
    var it = g(), ret;

    // asynchronously iterate over generator
    (function iterate(val){
        ret = it.next( val );

        if (!ret.done) {
            // poor man's "is it a promise?" test
            if ("then" in ret.value) {
                // wait on the promise
                ret.value.then( iterate );
            }
            // immediate value: just send right back in
            else {
                // avoid synchronous recursion
                setTimeout( function(){
                    iterate( ret.value );
                }, 0 );
            }
        }
    })();
}

次に、予想されるDOMミューテーションが発生する前の任意の時点で、を実行するだけrunGenerator(getMutation)です。

これで、DOMミューテーションを同期スタイルの制御フローに統合できます。なんてことだ。

于 2016-09-19T17:51:19.090 に答える
13

(下部にある再検討された回答を参照してください)

livequeryjQuery用のプラグインを使用できます。次のようなセレクター式を指定できます。

$("input[type=button].removeItemButton").livequery(function () {
    $("#statusBar").text('You may now remove items.');
});

removeItemButtonクラスのボタンが追加されるたびに、ステータスバーにメッセージが表示されます。

効率の観点からは、これを避けたいと思うかもしれませんが、いずれの場合でも、独自のイベントハンドラーを作成する代わりに、プラグインを活用できます。

再考された答え

上記の答えは、プラグインを介してアイテムがDOMに追加されたことを検出することだけを目的としていました。

ただし、ほとんどのjQuery.on()場合、次のようなアプローチの方が適切です。

$("#myParentContainer").on('click', '.removeItemButton', function(){
          alert($(this).text() + ' has been removed');
});

たとえば、クリックに応答する必要がある動的コンテンツがある場合は、を使用してイベントを親コンテナにバインドするのが最適jQuery.onです。

于 2011-10-20T04:46:57.767 に答える
4

正確にそれを行うこのプラグインをチェックしてください--jquery.initialize

.each関数とまったく同じように機能します。違いは、入力したセレクターを使用して、このセレクターと一致する将来追加される新しいアイテムを監視し、それらを初期化することです。

初期化は次のようになります

$(".some-element").initialize( function(){
    $(this).css("color", "blue");
});

ただし、新しい要素一致.some-elementセレクターがページに表示される場合は、すぐに初期化されます。

新しいアイテムを追加する方法は重要ではありません。コールバックなどを気にする必要はありません。

したがって、次のような新しい要素を追加する場合:

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

すぐに初期化されます。

プラグインはに基づいていますMutationObserver

于 2015-02-04T01:23:55.210 に答える
0

純粋なJavaScriptソリューション(なしjQuery):

const SEARCH_DELAY = 100; // in ms

// it may run indefinitely. TODO: make it cancellable, using Promise's `reject`
function waitForElementToBeAdded(cssSelector) {
  return new Promise((resolve) => {
    const interval = setInterval(() => {
      if (element = document.querySelector(cssSelector)) {
        clearInterval(interval);
        resolve(element);
      }
    }, SEARCH_DELAY);
  });
}

console.log(await waitForElementToBeAdded('#main'));
于 2018-12-15T09:09:25.117 に答える
0

jQueryを使用すると、次のことができます-

function nodeInserted(elementQuerySelector){
    if ($(elementQuerySelector).length===0){
        setTimeout(function(){
            nodeInserted(elementQuerySelector);
        },100);
    }else{
        $(document).trigger("nodeInserted",[elementQuerySelector]);
    }
}

関数は、ノードが見つかるまでノードを再帰的に検索し、ドキュメントに対してイベントをトリガーします

次に、これを使用して実装できます

nodeInserted("main");
$(document).on("nodeInserted",function(e,q){
    if (q === "main"){
        $("main").css("padding-left",0);
    }
});
于 2021-06-13T14:36:56.593 に答える