web-dev-qa-db-ja.com

要素がページに追加されたときに通知を受けるにはどうすればよいですか?

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

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

126
Kevin Burke

警告!

この答えは今では時代遅れです。 DOMレベル4では MutationObserver が導入され、非推奨の突然変異イベントの効果的な代替が提供されます。ここに提示されているものよりも優れたソリューションについては、 この回答 を参照してください。真剣に。 100ミリ秒ごとにDOMをポーリングしないでください。 CPUパワーを浪費し、ユーザーはあなたを嫌います。

mutation events は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(1/10)秒です。リアルタイムの要素観察が必要でない限り、それで十分です。

82
Jose Faeti

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

したがって、実際の実際の答えは、「突然変異オブザーバーを使用します。..最終的には。しかし、とりあえずホセ・ファエティの答えに進みます」:)

34
Asfand Qazi

mutation events の廃止から MutationObserver の出現までの間、特定の要素がDOMに追加されたときに通知される効率的な方法は CSS3アニメーションを活用することでしたイベント

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

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

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

18
jokeyrhyme

livequery jQueryのプラグインを使用できます。次のようなセレクター式を提供できます。

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

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

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

再訪した回答

上記の答えは、プラグインを通じてdetectがアイテムがDOMに追加されたことを意味するだけです。

ただし、ほとんどの場合、jQuery.on()アプローチがより適切です。次に例を示します。

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

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

13

ETA 24 Apr 17async/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のものを使用する必要があるため、互換性の問題を認識してください(つまり、視聴者のほとんどがアーミッシュ、luddite、またはさらに悪いことにIE8ユーザー)

まず、 MutationObserver API を使用して、オブザーバーオブジェクトを構築します。このオブジェクトを約束でラップし、コールバックが発生したときに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 to 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ミューテーションを同期スタイルの制御フローに統合できます。なんてことだ。

9
Brandon

Arriveと呼ばれる有望なjavascriptライブラリがあります。これは、ブラウザのサポートが一般的になったときに、ミューテーションオブザーバを活用するのに最適な方法のようです。

https://github.com/uzairfarooq/arrive/

8
Dave Kiss

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

3
pie6k

純粋な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'));
0
vedant