要素がページに追加されたときにどのように通知されますか?


139

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

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


あなたはあなたの別のスクリプトがページに入れた特定の要素、またはソースに関係なく追加された要素をチェックする必要がありますか?
ホセファエティ2011

1
他の誰かのコードに要素を追加する関数を知っていますか?そうであれば、それを上書きして、カスタムイベントをトリガーする1行を追加できます。
jeffreydev '09 / 09/15

回答:


86

警告!

この回答は古くなっています。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秒の1/10(1/10)です。リアルタイムの要素観測が必要でない限り、それで十分です。


1
ホセ、条件が実際に満たされたときにsettimeoutをどのように終了しますか?つまり、x秒後にページに最終的に読み込まれた要素を見つけましたか?
クレウィス2013

1
@blachawkでは、setTimeoutの戻り値を変数に割り当てる必要があります。これを後でパラメーターとしてclearTimeout()に渡して、変数をクリアできます。
ホセファエティ2013年

3
@blackhawk:私はその答えを見て+1しました。ただし、setTimeout()は1回しか実行されないため、ここでは実際にclearTimeout()を使用する必要がないことに注意してください。1つの「if-else」で十分です。挿入された要素を見つけたら、実行をsetTimeout()に渡さないでください。
1111161171159459134 2014年

使用に有用な実用的なガイドMutationObserver()davidwalsh.name/mutationobserver-api
gfullam

4
それは汚い。AS3に「相当する」ものは本当にありませんEvent.ADDED_TO_STAGEか?
Bitterblue

45

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

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


19

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

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

DOMノード挿入イベントを受け取りたいDOM要素を(選択したCSSセレクターを介して)対象とするCSSキーフレームシーケンスを設定します。私は比較的無害でほとんど使用されていないcssプロパティを使用しました。意図したページスタイルの混乱を避けるために、アウトラインカラーを使用しました。コードは一度クリッププロパティを対象としたものですが、バージョン11のIEではアニメーション化できなくなりました。とはいえ、アニメーション化できるプロパティはすべて機能するので、好きなものを選択してください。

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


これは最高のパフォーマンスのソリューションであり、このためのライブラリーについて言及している重複した質問に回答があります。
Dan Dascalescu、2015

1
過小評価された答え。
–GökhanKurt 2016

注意してください。ミリ秒の精度が重要な場合、このアプローチは正確ではありません。このjsbinはインラインコールバックと使用の間に30ms以上差があることを示してanimationstartjsbin.com/netuquralu/1/edit
Gajus 16

私はカートに同意します、これは過小評価されています。
Zabbu 2018

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です。


11

ETA 24 Apr 17 私はこれをもっと簡潔にするためにasync/ awaitマジックでこれを少し単純化したかった:

同じ約束されたオブザーバブルを使用する:

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ユーザーの場合)。

まず、MutationObserver APIを使用してオブザーバーオブジェクトを作成します。このオブジェクトを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ミューテーションを同期スタイルの制御フローに統合できます。なんてことだ。



3

それを正確に行うこのプラグインをチェックしてください-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


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'));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.