要素が存在するようになるまで待つ方法は?


237

私はChromeの拡張機能に取り組んでいて、疑問に思っています。要素が存在するようになったときにそれを見つけるための最良の方法は何ですか?プレーンなJavaScriptを使用し、要素が存在するまでチェックする間隔を設けていますか、それともjQueryはこれを行う簡単な方法を持っていますか?


1
今日ここにあるすべてのオプション(コメントを含む)は古くなっているか、不完全です。彼らは、@ hughskのすばらしい入力である互換性の議論を完全には考慮していません。その間、私は一般的な単純さとオーバーヘッドのリスクを減らすために、ライアンの答えにブランドンの更新を単に使用することをお勧めします。
cregox 2015

4
MutationObserver> DOM Mutation Events> setTimeout
mattsven 2015

2
私が立っているところからではありません。setTimeout互換性があり、実装が簡単で、保守が簡単で、オーバーヘッドはほとんどありません。
cregox

setTimeout+ jQuery2つの理由で、私の意見では理想的とは言えません:1.)jQuery膨張2.)要素をDOMに手動でクエリする必要があり、イベントが速度的に簡単にビートする3.)ネイティブよりも常に遅い実装。要素の存在に基づいてかなり迅速に何かを実行する必要がある場合、特にシームレスなユーザーエクスペリエンスが目標である場合、それは劣ります。
mattsven 2015

3
カウントできる人とカウントできない人の3種類があります。; P
cregox 2015

回答:


149

DOMNodeInsertedパフォーマンスの問題のため、他のDOM ミューテーションイベントとともに非推奨になっています-推奨されるアプローチは、MutationObserverを使用してDOMを監視することです。ただし、新しいブラウザでのみサポートされているためDOMNodeInsertedMutationObserverが利用できない場合は、フォールバックする必要があります。

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if (!mutation.addedNodes) return

    for (var i = 0; i < mutation.addedNodes.length; i++) {
      // do things to your newly added nodes here
      var node = mutation.addedNodes[i]
    }
  })
})

observer.observe(document.body, {
    childList: true
  , subtree: true
  , attributes: false
  , characterData: false
})

// stop watching using:
observer.disconnect()

50
常にMutationObserver APIが少し複雑であることを発見したので、要素の作成/削除をリッスンするためのより単純なAPIを提供するために、ライブラリ、arrival.jsを構築しました。
Uzair Farooq 2014

15
@UzairFarooqの優れたライブラリgithub.com/uzairfarooq/arriveの
Dennis

3
注意すべき2つの点:(1)空の配列であってもtrueを返すif (mutation.addedNodes.length)ため、if (mutation.addedNodes)そうする方が良いでしょう。(2)mutation.addedNodes.forEach()addedNodesはnodeListであり、forEachを使用してnodeListを反復処理できないため、実行できません。これの解決策については、toddmotto.com / ditch
the

3
これをどのように使用するかの例を挙げられますか?DOM要素が存在するときに実行したいjqueryセレクターまたはコードをどこに置くかわからない。
Superdooperhero

1
@Superdooperhero簡単な例で答えました。確認してください。stackoverflow.com/a/57395241/6542186
SilverSurfer

113

私も同じ問題を抱えていたので、先に進んでプラグインを作成しました。

$(selector).waitUntilExists(function);

コード:

;(function ($, window) {

var intervals = {};
var removeListener = function(selector) {

    if (intervals[selector]) {

        window.clearInterval(intervals[selector]);
        intervals[selector] = null;
    }
};
var found = 'waitUntilExists.found';

/**
 * @function
 * @property {object} jQuery plugin which runs handler function once specified
 *           element is inserted into the DOM
 * @param {function|string} handler 
 *            A function to execute at the time when the element is inserted or 
 *            string "remove" to remove the listener from the given selector
 * @param {bool} shouldRunHandlerOnce 
 *            Optional: if true, handler is unbound after its first invocation
 * @example jQuery(selector).waitUntilExists(function);
 */

$.fn.waitUntilExists = function(handler, shouldRunHandlerOnce, isChild) {

    var selector = this.selector;
    var $this = $(selector);
    var $elements = $this.not(function() { return $(this).data(found); });

    if (handler === 'remove') {

        // Hijack and remove interval immediately if the code requests
        removeListener(selector);
    }
    else {

        // Run the handler on all found elements and mark as found
        $elements.each(handler).data(found, true);

        if (shouldRunHandlerOnce && $this.length) {

            // Element was found, implying the handler already ran for all 
            // matched elements
            removeListener(selector);
        }
        else if (!isChild) {

            // If this is a recurring search or if the target has not yet been 
            // found, create an interval to continue searching for the target
            intervals[selector] = window.setInterval(function () {

                $this.waitUntilExists(handler, shouldRunHandlerOnce, true);
            }, 500);
        }
    }

    return $this;
};

}(jQuery, window));

5
プラグインをありがとう。フォークして少し改善しました。私のアップデートから好きなものを自由に使ってください。さらにいくつかの改善を計画していますが、まだ更新済みプラグイン
Brandon Belvin

8
jquery depがなくてもいいでしょう...;)
knutole '25 / 07/25

4
多分あなたはそれがどのように機能するか言及する必要があります:それは要素が存在するかどうかを500ミリ秒ごとに尋ねることによって機能します(を使用してwindow.setInterval)。MutationObserver投票でも回答が機能するかどうかはわかりません...
スポーツ

2
要素が既にページ上にある場合は、正しく機能しません。この関数の適切なバージョンは次のとおり
RolandSoós

2
;関数(;(function ($, window) {)の始めに何が使われているのか説明してもらえますか?
mrid

76

要素の表示を待つコアJavaScript関数は次のとおりです。

パラメーター:

  1. selector:この関数は要素$ {selector}を探します
  2. time:この関数は、この要素が$ {time}ミリ秒ごとに存在するかどうかを確認します。

    function waitForElementToDisplay(selector, time) {
            if(document.querySelector(selector)!=null) {
                alert("The element is displayed, you can put your code instead of this alert.")
                return;
            }
            else {
                setTimeout(function() {
                    waitForElementToDisplay(selector, time);
                }, time);
            }
        }

一例として、設定selector="#div1"time=5000そのHTMLタグを探しますid="div1"ごとに5000ミリ秒。


いいね!セレクタを受け入れることができるようにこれを書くことができますか?
mattsven

私はそれを行うことができます疑う..しかしgetElementByXpathを取得するには、この記事を見てください。stackoverflow.com/questions/10596417/...
エティエンヌTonnelier


1
代わりにミューテーションオブザーバーを使用するように記述できますか?
SuperUberDuper

または、これをプロミスを使用するように書き換えることができますか?
SuperUberDuper

25

あなたが聞くことができますDOMNodeInsertedか、DOMSubtreeModified新しい要素がDOMに追加されるたびに発生するイベントイベントをます。

新しい要素が作成されたことを検出するLiveQuery jQueryプラグインもあります。

$("#future_element").livequery(function(){
    //element created
});

1
とても素敵なプラグイン!そのような関数はjqueryに直接ありますか?それを行うための既存の機能はないのでしょうか。そして、これがプラグインの場合は、この回答に投票してください;)私にとっては、完全に機能します。どうもありがとうございました。
Samuel

1
注IE 9はDOMNodeInsertedを実装していますが、要素を追加したときに起動しないという大きなバグがあります。これは、ほとんどの場合それを使用したい場合です。詳細は、help.dottoro.com
ljmcxjla.phpにあります。mikemaccana12年

23

このアプローチを使用して、要素が表示されるのを待ってから、他の関数を実行できるようにしました。

doTheRestOfTheStuff(parameters)IDの要素がthe_Element_ID表示されるか、読み込みが完了した後にのみ関数を呼び出す必要があるとしましょう。

var existCondition = setInterval(function() {
 if ($('#the_Element_ID').length) {
    console.log("Exists!");
    clearInterval(existCondition);
    doTheRestOfTheStuff(parameters);
 }
}, 100); // check every 100ms

21

できるよ

$('#yourelement').ready(function() {

});

これは、サーバーからリクエストされたときに要素がDOMに存在する場合にのみ機能することに注意してください。要素がJavaScriptを介して動的に追加されている場合、機能せず、他の回答を確認する必要がある場合があります。


7
この.ready()関数は、だけでなく、ほとんど何でも機能しますdocument。動的に作成された要素では機能しません.live()
Richard Neil Ilagan

7
@Bery、リチャードが指摘したように、これは、サーバーから最初に要求されたときにHTMLにすでに存在する要素に対してのみ機能します。JavaScriptを使用してDOMに動的に要素を追加する場合、それは機能しません。
チャンドランシュ2013年

6
@Sam、それをメモリ内の要素の参照に添付する方法を明確にしていただけませんか?
Vikas Singhal 2014

3
この答えは間違っています。ここで実際にチェックしているのは通常のもの$(document).ready()であり、適用すると思われる要素ではありません。それが、この特別なリスナーが機能する方法です。
シッケディエル

1
この使い方は、に応じて推奨されていませんapi.jquery.com/ready
splintor

14

簡単で読みやすい作業例では、まだ答えはありません。MutationObserver interface を使用して、次のようにDOMの変更を検出します。

var observer = new MutationObserver(function(mutations) {
    if ($("p").length) {
        console.log("Exist, lets do something");
        observer.disconnect(); 
        //We can disconnect observer once the element exist if we dont want observe more changes in the DOM
    }
});

// Start observing
observer.observe(document.body, { //document.body is node target to observe
    childList: true, //This is a must have for the observer with subtree
    subtree: true //Set to true if changes must also be observed in descendants.
});
            
$(document).ready(function() {
    $("button").on("click", function() {
        $("p").remove();
        setTimeout(function() {
            $("#newContent").append("<p>New element</p>");
        }, 2000);
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<button>New content</button>
<div id="newContent"></div>

注: 詳細については、スペイン語のMozillaドキュメントをMutationObserver参照してください。


2
投票の理由を説明するコメントを残すことを検討してください。そうすれば、回答を改善できます。ありがとう。
SilverSurfer

12

必要なセレクタを追加するだけです。要素が見つかったら、コールバック関数でアクセスできます。

const waitUntilElementExists = (selector, callback) => {
const el = document.querySelector(selector);

if (el){
    return callback(el);
}

setTimeout(() => waitUntilElementExists(selector, callback), 500);
}

waitUntilElementExists('.wait-for-me', (el) => console.log(el));

2
PossessWithin内で、これは非常にクリーンなソリューションであり、私のために機能します。
jstafford

3
この回答は、IE8-10および最新のブラウザで機能します。主な問題は、要素が存在しない場合でも実行し続けることです。そのため、要素が存在することが確実な場合に最適です。それ以外の場合は、カウンターを追加できます。
名前の場合

1
完璧に機能した
James Stewart

1
チャームのように働いた!!
アマン、

1
それらは類似しており、同一ではありませんでした。さらに、多くの人々が同じことをしています。最後に、このソリューションを自分でコーディングしました。それは不十分な推論ですが、実際にそうだったとしても、コメントで教えていただければ幸いです。答えはOPの問題を解決し、反対票を投じる明確な動機はありません。
ディエゴフォルテス

11

jQueryを使用した単純なアプローチの場合、これがうまく機能することがわかりました。

  // Wait for element to exist.
  function elementLoaded(el, cb) {
    if ($(el).length) {
      // Element is now loaded.
      cb($(el));
    } else {
      // Repeat every 500ms.
      setTimeout(function() {
        elementLoaded(el, cb)
      }, 500);
    }
  };

  elementLoaded('.element-selector', function(el) {
    // Element is ready to use.
    el.click(function() {
      alert("You just clicked a dynamically inserted element");
    });
  });

ここでは、500msごとに単に要素が読み込まれているかどうかを確認し、読み込まれている場合はそれを使用できます。

これは、ドキュメントに動的に追加された要素にクリックハンドラーを追加する場合に特に便利です。


8

InsertionQueryはどうですかライブラリ?

InsertionQueryは、指定されたセレクタにアタッチされたCSSアニメーションコールバックを使用して、要素の作成時にコールバックを実行します。このメソッドを使用すると、初めてではなく、要素が作成されるたびにコールバックを実行できます。

githubから:

表示されているノードをキャッチする非DOMイベントの方法。そして、それはセレクターを使用します。

これは、幅広いブラウザーサポートのためだけではなく、特定のことについてはDOMMutationObserverよりも優れている場合があります。

どうして?

  • DOMイベントはブラウザの速度を低下させ、insertionQueryはそうしないため
  • DOM Mutation Observerは、insertionQueryよりもブラウザのサポートが少ないため
  • InsertQueryを使用すると、パフォーマンスのオーバーヘッドなしにセレクターを使用してDOMの変更をフィルター処理できるためです。

幅広いサポート!

IE10 +およびほとんどすべて(モバイルを含む)


7

以下は、MutationObserverの薄いラッパーとして機能する関数です。唯一の要件は、ブラウザがMutationObserverをサポートしていることです。JQueryへの依存関係はありません。以下のスニペットを実行して、実際の例を確認してください。

function waitForMutation(parentNode, isMatchFunc, handlerFunc, observeSubtree, disconnectAfterMatch) {
  var defaultIfUndefined = function(val, defaultVal) {
    return (typeof val === "undefined") ? defaultVal : val;
  };

  observeSubtree = defaultIfUndefined(observeSubtree, false);
  disconnectAfterMatch = defaultIfUndefined(disconnectAfterMatch, false);

  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.addedNodes) {
        for (var i = 0; i < mutation.addedNodes.length; i++) {
          var node = mutation.addedNodes[i];
          if (isMatchFunc(node)) {
            handlerFunc(node);
            if (disconnectAfterMatch) observer.disconnect();
          };
        }
      }
    });
  });

  observer.observe(parentNode, {
    childList: true,
    attributes: false,
    characterData: false,
    subtree: observeSubtree
  });
}

// Example
waitForMutation(
  // parentNode: Root node to observe. If the mutation you're looking for
  // might not occur directly below parentNode, pass 'true' to the
  // observeSubtree parameter.
  document.getElementById("outerContent"),
  // isMatchFunc: Function to identify a match. If it returns true,
  // handlerFunc will run.
  // MutationObserver only fires once per mutation, not once for every node
  // inside the mutation. If the element we're looking for is a child of
  // the newly-added element, we need to use something like
  // node.querySelector() to find it.
  function(node) {
    return node.querySelector(".foo") !== null;
  },
  // handlerFunc: Handler.
  function(node) {
    var elem = document.createElement("div");
    elem.appendChild(document.createTextNode("Added node (" + node.innerText + ")"));
    document.getElementById("log").appendChild(elem);
  },
  // observeSubtree
  true,
  // disconnectAfterMatch: If this is true the hanlerFunc will only run on
  // the first time that isMatchFunc returns true. If it's false, the handler
  // will continue to fire on matches.
  false);

// Set up UI. Using JQuery here for convenience.

$outerContent = $("#outerContent");
$innerContent = $("#innerContent");

$("#addOuter").on("click", function() {
  var newNode = $("<div><span class='foo'>Outer</span></div>");
  $outerContent.append(newNode);
});
$("#addInner").on("click", function() {
  var newNode = $("<div><span class='foo'>Inner</span></div>");
  $innerContent.append(newNode);
});
.content {
  padding: 1em;
  border: solid 1px black;
  overflow-y: auto;
}
#innerContent {
  height: 100px;
}
#outerContent {
  height: 200px;
}
#log {
  font-family: Courier;
  font-size: 10pt;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Create some mutations</h2>
<div id="main">
  <button id="addOuter">Add outer node</button>
  <button id="addInner">Add inner node</button>
  <div class="content" id="outerContent">
    <div class="content" id="innerContent"></div>
  </div>
</div>
<h2>Log</h2>
<div id="log"></div>


6

これは、バニラJavaScriptでの約束を返すソリューションです(乱雑なコールバックはありません)。デフォルトでは200msごとにチェックします。

function waitFor(selector) {
    return new Promise(function (res, rej) {
        waitForElementToDisplay(selector, 200);
        function waitForElementToDisplay(selector, time) {
            if (document.querySelector(selector) != null) {
                res(document.querySelector(selector));
            }
            else {
                setTimeout(function () {
                    waitForElementToDisplay(selector, time);
                }, time);
            }
        }
    });
}

5

何かを待つことができる純粋なJavascript関数を次に示します。間隔を長く設定して、CPUリソースを節約します。

/**
 * @brief Wait for something to be ready before triggering a timeout
 * @param {callback} isready Function which returns true when the thing we're waiting for has happened
 * @param {callback} success Function to call when the thing is ready
 * @param {callback} error Function to call if we time out before the event becomes ready
 * @param {int} count Number of times to retry the timeout (default 300 or 6s)
 * @param {int} interval Number of milliseconds to wait between attempts (default 20ms)
 */
function waitUntil(isready, success, error, count, interval){
    if (count === undefined) {
        count = 300;
    }
    if (interval === undefined) {
        interval = 20;
    }
    if (isready()) {
        success();
        return;
    }
    // The call back isn't ready. We need to wait for it
    setTimeout(function(){
        if (!count) {
            // We have run out of retries
            if (error !== undefined) {
                error();
            }
        } else {
            // Try again
            waitUntil(isready, success, error, count -1, interval);
        }
    }, interval);
}

たとえばjQueryでこれを呼び出すには、次のようなものを使用します。

waitUntil(function(){
    return $('#myelement').length > 0;
}, function(){
    alert("myelement now exists");
}, function(){
    alert("I'm bored. I give up.");
});

3

を返し、Promiseタイムアウトを使用できるようにするソリューション(互換性のあるIE 11以降)。

単一の要素(要素タイプ)の場合:

"use strict";

function waitUntilElementLoaded(selector) {
    var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    var start = performance.now();
    var now = 0;

    return new Promise(function (resolve, reject) {
        var interval = setInterval(function () {
            var element = document.querySelector(selector);

            if (element instanceof Element) {
                clearInterval(interval);

                resolve();
            }

            now = performance.now();

            if (now - start >= timeout) {
                reject("Could not find the element " + selector + " within " + timeout + " ms");
            }
        }, 100);
    });
}

複数の要素(NodeListタイプ)の場合:

"use strict";

function waitUntilElementsLoaded(selector) {
    var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    var start = performance.now();
    var now = 0;

    return new Promise(function (resolve, reject) {
        var interval = setInterval(function () {
            var elements = document.querySelectorAll(selector);

            if (elements instanceof NodeList) {
                clearInterval(interval);

                resolve(elements);
            }

            now = performance.now();

            if (now - start >= timeout) {
                reject("Could not find elements " + selector + " within " + timeout + " ms");
            }
        }, 100);
    });
}

例:

waitUntilElementLoaded('#message', 800).then(function(element) {
    // element found and available

    element.innerHTML = '...';
}).catch(function() {
    // element not found within 800 milliseconds
});

waitUntilElementsLoaded('.message', 10000).then(function(elements) {
    for(const element of elements) {
        // ....
    }
}).catch(function(error) {
    // elements not found withing 10 seconds
});

要素のリストと単一の要素の両方で機能します。


1
私のお気に入りのソリューション!なぜチェックするのelement instanceof HTMLElementですか?それは、これまで以上に他の何もすることができますnullHTMLElement
Leeroy

1
あなたは興味深い点を上げます。Element代わりに(固定)を使用して、それをより広くする必要がありました。Element MDNのドキュメントに記載さelementているように、変数にプロパティがあることを確認したいので、チェックを行います。気にならない場合は、気軽に外してください!innerHTML
アンワル

2

MutationObserverを使用したわかりやすい例:

new MutationObserver( mutation => {
    if (!mutation.addedNodes) return
    mutation.addedNodes.forEach( node => {
        // do stuff with node
    })
})

2

これは、約束に慣れていて、サードパーティのライブラリやタイマーを使いたくない人のためのシンプルなソリューションです。

私のプロジェクトでしばらく使用しています

function waitForElm(selector) {
    return new Promise(resolve => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver(mutations => {
            if (document.querySelector(selector)) {
                resolve(document.querySelector(selector));
                observer.disconnect();
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
}

それを使用するには:

waitForElm('.some-class').then(elm => console.log(elm.textContent));

または非同期/待機

const elm = await waitForElm('.some-classs')

これはすごいです!それについてのクールな部分は、あなたがasync/ awaitでもそれを使うことができたということです。また、次のようにすることで、より多くのパフォーマンスをmutations.addedNodes.find(node => node.matchesSelector("..."))
引き出せる

@mattsven良い点!ミューテーションのノードのみをチェックする方が、document.querySelectorを実行するよりもパフォーマンスが高くなります。
Yong Wang

スペルミスを修正して、watiForElmをwaitForElmに修正してください
dalvir

1

しばらくして(タイムアウト)後の検索を停止する場合は、次のjQueryが機能します。10秒後にタイムアウトします。名前で入力を選択する必要があり、他のいくつかのソリューションの実装に問題があったため、純粋なJSではなくこのコードを使用する必要がありました。

 // Wait for element to exist.

    function imageLoaded(el, cb,time) {

        if ($(el).length) {
            // Element is now loaded.

            cb($(el));

            var imageInput =  $('input[name=product\\[image_location\\]]');
            console.log(imageInput);

        } else if(time < 10000) {
            // Repeat every 500ms.
            setTimeout(function() {
               time = time+500;

                imageLoaded(el, cb, time)
            }, 500);
        }
    };

    var time = 500;

    imageLoaded('input[name=product\\[image_location\\]]', function(el) {

     //do stuff here 

     },time);

0

通常、このスニペットはタグマネージャーに使用します。

<script>
(function exists() {
  if (!document.querySelector('<selector>')) {
    return setTimeout(exists);
  }
  // code when element exists
})();  
</script>

0

非同期のdom変更がある場合、この関数はDOM要素を(秒単位の時間制限付きで)チェックします。DOMとそのPromiseベースでは重くなりません:)

function getElement(selector, i = 5) {
  return new Promise(async (resolve, reject) => {
    if(i <= 0) return reject(`${selector} not found`);
    const elements = document.querySelectorAll(selector);
    if(elements.length) return resolve(elements);
    return setTimeout(async () => await getElement(selector, i-1), 1000);
  })
}

// Now call it with your selector

try {
  element = await getElement('.woohoo');
} catch(e) { // catch the e }

//OR

getElement('.woohoo', 5)
.then(element => { // do somthing with the elements })
.catch(e => { // catch the error });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.