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


当前回答

对于一个使用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");
    });
  });

在这里,我们只需每500毫秒检查一次元素是否加载,当加载成功时,我们就可以使用它。

这对于向已动态添加到文档中的元素添加单击处理程序特别有用。

其他回答

这是写在王勇答案(最高分答案)上面的一个更好的版本。

增加的特性:您可以等待一个元素特定的时间,精确定位,以提高性能。

async function waitForElement(selector, timeout = null, location = document.body) {
    return new Promise((resolve) => {
        let element = document.querySelector(selector);
        if (element) {
            return resolve(element);
        }

        const observer = new MutationObserver(async () => {
            let element = document.querySelector(selector);
            if (element) {
                resolve(element);
                observer.disconnect();
            } else {
                if (timeout) {
                    async function timeOver() {
                        return new Promise((resolve) => {
                            setTimeout(() => {
                                observer.disconnect();
                                resolve(false);
                            }, timeout);
                        });
                    }
                    resolve(await timeOver());
                }
            }
        });

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

用法:

await waitForElement(".nav-alt", 500, ".main-body")

奖励:等待一个元素从DOM中消失。

async function waitForElementDeath(selector, location = document.body) {
    return new Promise((resolve) => {
        const observer = new MutationObserver(async () => {
            if (!document.querySelector(selector)) {
                resolve(true);
                observer.disconnect();
            }
        });

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

用法:

await waitForElementDeath(".Popup-div", "Popup-Container")

由于性能问题,DOMNodeInserted和其他DOM突变事件已被弃用——推荐的方法是使用MutationObserver监视DOM。不过,它只在较新的浏览器中受支持,所以当MutationObserver不可用时,您应该回到DOMNodeInserted上。

let observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (!mutation.addedNodes) return

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

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

// stop watching using:
observer.disconnect()

我使用这种方法等待一个元素出现,这样我就可以在那之后执行其他函数。

让我们说doTheRestOfTheStuff(参数)函数应该只在ID为the_Element_ID的元素出现或完成加载后调用,我们可以使用,

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

如果可以的话,我会尽量避开突变观察者,所以我想到了这个。它看起来类似于上面的一些其他答案。该函数将查找给定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的薄包装器。唯一的要求是浏览器支持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>