我正在Chrome中开发一个扩展,我想知道:当一个元素出现时,最好的方法是什么?使用纯javascript,间隔检查,直到一个元素存在,或jQuery有一些简单的方法来做到这一点?


当前回答

你可以试试这个:

const wait_until_element_appear = setInterval(() => {
    if ($(element).length !== 0) {
        // some code
        clearInterval(wait_until_element_appear);
    }
}, 0);

这个办法对我很有效

其他回答

更新

下面是一个更新的版本,可以使用承诺。如果达到特定的尝试次数,它也会“停止”。

function _waitForElement(selector, delay = 50, tries = 100) {
    const element = document.querySelector(selector);

    if (!window[`__${selector}`]) {
      window[`__${selector}`] = 0;
      window[`__${selector}__delay`] = delay;
      window[`__${selector}__tries`] = tries;
    }

    function _search() {
      return new Promise((resolve) => {
        window[`__${selector}`]++;
        setTimeout(resolve, window[`__${selector}__delay`]);
      });
    }

    if (element === null) {
      if (window[`__${selector}`] >= window[`__${selector}__tries`]) {
        window[`__${selector}`] = 0;
        return Promise.resolve(null);
      }

      return _search().then(() => _waitForElement(selector));
    } else {
      return Promise.resolve(element);
    }
  }

用法很简单,用await使用它只是确保你在一个 异步功能:

const start = (async () => {
  const $el = await _waitForElement(`.my-selector`);
  console.log($el);
})();

过时的版本

只需添加所需的选择器。一旦找到元素,就可以在回调函数中访问它。

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));

一个返回承诺的解决方案,并允许使用超时(兼容IE 11+)。

对于单个元素(element类型):

"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
});

既适用于元素列表,也适用于单个元素。

如果可以的话,我会尽量避开突变观察者,所以我想到了这个。它看起来类似于上面的一些其他答案。该函数将查找给定DOM调用中存在的第一个元素——className是预期的用法,但它也可以接受tagName或Id。如果您正在寻找具有给定类名或标记名的元素数量,则还可以为精确索引添加参数。

    async function waitUntilElementExits(domkey,domquery,maxtime){
        const delay = (ms) => new Promise(res => setTimeout(res, ms));
        for(let i=0; i<maxtime; i=i+200){
            await delay(200);
            let elm = document[domkey](domquery);
            if( (domkey == 'getElementById' && elm) || elm?.[0] ) break;
        }
    }
    // usage
    await waitUntilElementExits('getElementByClassName','some_class_name',10000)

一个使用MutationObserver的更清晰的例子:

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

下面是一个函数,充当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>