如何等到元素存在?

mat*_*ven 213 javascript jquery google-chrome google-chrome-extension

我正在研究Chrome中的扩展程序,我想知道:什么是找出元素何时出现的最佳方法?使用普通的javascript,检查直到元素存在的间隔,或jQuery有一些简单的方法来做到这一点?

hug*_*hsk 137

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()
Run Code Online (Sandbox Code Playgroud)

  • 我总是发现MutationObserver api有点复杂,所以我建了一个库,[arri.js](https://github.com/uzairfarooq/arrive),提供一个更简单的API来监听元素的创建/删除. (42认同)
  • 我建议使用@UzairFarooq优秀的库https://github.com/uzairfarooq/arrive (14认同)
  • 有两点需要注意:(1)最好做`if(mutation.addedNodes.length)`因为`if(mutation.addedNodes)`仍然会返回true,即使它是一个空数组.(2)你不能做`mutation.addedNodes.forEach()`因为addedNodes是一个nodeList而你不能用forEach迭代一个nodeList.有关此问题的解决方案,请参阅http://toddmotto.com/ditch-the-array-foreach-call-nodelist-hack/ (3认同)
  • 您能举一个例子说明如何使用它吗?当DOM元素存在时,不确定将我的jquery选择器或要执行的代码放在哪里。 (3认同)

Rya*_*ter 112

我遇到了同样的问题,所以我继续为它写了一个插件.

$(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));
Run Code Online (Sandbox Code Playgroud)

  • 没有jquery dep也会很好...;) (7认同)
  • 谢谢你的插件.我分叉并改进了一下.随意从我的更新中获取您想要的任何内容.我还计划了一些改进:[更新插件](https://gist.github.com/PizzaBrandon/5709010) (5认同)
  • 也许你应该提一下它是如何工作的:如果元素存在则使用每500毫秒(使用`window.setInterval`).我不知道`MutationObserver`答案是否也适用于民意调查...... (4认同)
  • 如果元素已经在页面上,它将无法正常工作.以下是此功能的正确版本:https://gist.github.com/PizzaBrandon/5709010 (2认同)
  • 您能否解释一下函数(`;(function($(window){`))的开头是什么? (2认同)

Eti*_*ier 65

这是一个等待元素显示的核心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);
            }
        }
    
    Run Code Online (Sandbox Code Playgroud)

作为一个例子,设置selector="#div1"time=5000会寻找HTML标签,其id="div1"每5000毫秒.


Yon*_*ang 36

对于那些习惯于承诺并且不想使用任何第三方库或计时器的人来说,这是一个简单的解决方案。

我已经在我的项目中使用它一段时间了

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
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

要使用它:

waitForElm('.some-class').then(elm => console.log(elm.textContent));
Run Code Online (Sandbox Code Playgroud)

或使用异步/等待

const elm = await waitForElm('.some-classs')
Run Code Online (Sandbox Code Playgroud)

  • 这很整洁!它最酷的部分是你也可以将它与“async”/“await”一起使用。您还可以通过执行 `mutations.addedNodes.find(node =&gt; node.matchesSelector("..."))` 来获得更多性能 (15认同)
  • @RalphDavidAbernathy,你是对的,代码中未使用“mutations”参数,可以安全删除。它有很多关于突变的有用信息。我把它放在那里以防万一您需要访问它。 (3认同)
  • @Julix,if-return 语句是针对调用此函数时元素已经存在的情况的优化。Promise 将直接被解决,而不必与 MutationObserver 发生冲突 (3认同)
  • 这个函数正在检查第一次进入 DOM 的项目,如何检查项目是否出现多次,所以每次都应该运行, (2认同)
  • 这似乎覆盖了监视目标元素事件的任何其他代码。我有一个页面,我试图根据我无法控制的其他代码(在我自己的用户脚本中,监视网站的表单,有条件地应用格式)来监视输入的更改。上面的代码会导致页面自身的 JS 停止对目标元素运行。有没有什么方法可以监视元素,**不会破坏**任何现有的事件观察器?也许类似于 jQuery 的 `noConflict()`? (2认同)

ser*_*erg 24

每当将新元素添加到DOM时,您都可以监听DOMNodeInsertedDOMSubtreeModified触发事件.

还有LiveQuery jQuery插件可以检测何时创建新元素:

$("#future_element").livequery(function(){
    //element created
});
Run Code Online (Sandbox Code Playgroud)

  • **弃权警告**(见接受的答案.) (9认同)

Sil*_*fer 23

我认为这里仍然没有任何简单易读的工作示例的答案。使用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);
    });
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<button>New content</button>
<div id="newContent"></div>
Run Code Online (Sandbox Code Playgroud)

注意: 如果您想了解更多信息,西班牙语 Mozilla文档MutationObserver会更详细。

  • 考虑发表评论解释投票的原因,以便我可以改进我的答案。谢谢。 (3认同)

Spl*_*ynx 22

你可以做

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

});
Run Code Online (Sandbox Code Playgroud)

请注意,这仅在从服务器请求时元素存在于DOM中时才有效.如果通过JavaScript动态添加元素,它将无法工作,您可能需要查看其他答案.

  • `.ready()`函数适用于大多数任何东西(如果不是什么),而不仅仅是`document`.它只是不能用于动态创建的元素,即使在`.live()`上也是如此. (7认同)
  • 正如Richard所指出的那样,@ Bery只适用于首次从服务器请求时已存在于HTML中的元素.如果使用Javascript动态地向DOM添加元素,则它不起作用. (7认同)
  • @Sam,您能否澄清一下如何将它附加到内存中元素的引用? (6认同)
  • 这个答案是不正确的.你在这里检查的是一个普通的`$(document).ready()`,而不是你认为它也适用的元素.这就是这个*特殊*听众的工作方式.[实施例](http://codepen.io/anon/pen/yeqxdV?editors=0010) (3认同)
  • 根据 https://api.jquery.com/ready/ 不推荐这种用法 (2认同)

pri*_*ime 17

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

假设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
Run Code Online (Sandbox Code Playgroud)


Hed*_*ith 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");
    });
  });
Run Code Online (Sandbox Code Playgroud)

在这里,我们只需检查每500ms以查看元素是否已加载,何时加载,我们可以使用它.

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


b3w*_*wii 8

怎么样insertionQuery库?

insertionQuery使用附加到指定的选择器的CSS动画回调来在创建元素时运行回调.此方法允许在创建元素时运行回调,而不仅仅是第一次.

来自github:

捕获节点的非dom-event方式.它使用选择器.

它不仅仅适用于更广泛的浏览器支持,对于某些事情来说,它可能比DOMMutationObserver更好.

为什么?

  • 因为DOM事件会降低浏览器的速度,而insertQuery则不会
  • 因为DOM Mutation Observer比insertQuery具有更少的浏览器支持
  • 因为使用insertQuery,您可以使用选择器过滤DOM更改,而不会产生性能开销!

广泛的支持!

IE10 +以及其他任何东西(包括手机)


小智 8

你可以试试这个:

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

这个解决方案对我来说非常有用

  • 干净简洁。您可能想增加间隔,即。到 500ms,也许添加一个重试计数器以避免无限循环。 (2认同)

Pos*_*hin 7

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

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));
Run Code Online (Sandbox Code Playgroud)

  • 我想知道为什么这被低估了。 (2认同)
  • PossessWithin同意,这是一个非常干净的解决方案,对我有用。 (2认同)
  • 此答案适用于IE8-10以及现代浏览器。主要问题是,如果该元素不存在,它将继续运行-因此,当您确定该元素将在那里时,这是最好的方法。否则,您可以添加一个计数器。 (2认同)
  • 它们很相似,但并不相同。而且,很多人也在做同样的事情。最后,我自己编写了这个解决方案。这是一个糟糕的推理,但是,即使事实确实如此,我也希望您能发表评论让我知道。答案解决了OP的问题,并且没有明显的动机被否决。 (2认同)

Mr.*_*irl 7

下面的函数observe将允许您通过选择器监听元素。

在下面的示例中,2 秒过去后,a.greeting将被插入到.container. 由于我们正在侦听此元素的插入,因此我们可以有一个在插入时触发的回调。

const observe = (selector, callback, targetNode = document.body) =>
  new MutationObserver(mutations => [...mutations]
    .flatMap((mutation) => [...mutation.addedNodes])
    .filter((node) => node.matches && node.matches(selector))
    .forEach(callback))
  .observe(targetNode, { childList: true, subtree: true });

const createGreeting = () => {
  const el = document.createElement('DIV');
  el.textContent = 'Hello World';
  el.classList.add('greeting');
  return el;
};

const container = document.querySelector('.container');

observe('.greeting', el => console.log('I have arrived!', el), container);

new Promise(res => setTimeout(() => res(createGreeting()), 2000))
  .then(el => container.appendChild(el));
Run Code Online (Sandbox Code Playgroud)
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
body { display: flex; }
.container { display: flex; flex: 1; align-items: center; justify-content: center; }
.greeting { font-weight: bold; font-size: 2em; }
Run Code Online (Sandbox Code Playgroud)
<div class="container"></div>
Run Code Online (Sandbox Code Playgroud)


更新

这是一个实验性的异步/等待示例。

const sleep = (ms) => new Promise((res) => setTimeout(res, ms));

const observe = (selector, targetNode = document.body) =>
  new Promise(res => {
    new MutationObserver(mutations =>
      res([...mutations]
        .flatMap((mutation) => [...mutation.addedNodes])
        .find((node) => node.matches && node.matches(selector))))
    .observe(targetNode, { childList: true, subtree: true });
  });

const createGreeting = () => {
  const el = document.createElement('DIV');
  el.textContent = 'Hello World';
  el.classList.add('greeting');
  return el;
};

const container = document.querySelector('.container');

observe('.greeting', container)
  .then(el => console.log('I have arrived!', el));

(async () => {
  await sleep(2000);
  container.appendChild(createGreeting());
})();
Run Code Online (Sandbox Code Playgroud)
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
body { display: flex; }
.container { display: flex; flex: 1; align-items: center; justify-content: center; }
.greeting { font-weight: bold; font-size: 2em; }
Run Code Online (Sandbox Code Playgroud)
<div class="container"></div>
Run Code Online (Sandbox Code Playgroud)


Iva*_*jas 6

这是一个函数,它充当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);
});
Run Code Online (Sandbox Code Playgroud)
.content {
  padding: 1em;
  border: solid 1px black;
  overflow-y: auto;
}
#innerContent {
  height: 100px;
}
#outerContent {
  height: 200px;
}
#log {
  font-family: Courier;
  font-size: 10pt;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


xgr*_*sch 6

这是一个纯 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);
}
Run Code Online (Sandbox Code Playgroud)

要调用它,例如在 jQuery 中,请使用以下内容:

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


bla*_*ter 6

这是使用原始Javascript的Promise返回解决方案(没有混乱的回调)。默认情况下,它每200毫秒检查一次。

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);
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我通常将这段代码用于跟踪代码管理器:

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


归档时间:

查看次数:

219097 次

最近记录:

5 年,9 月 前