元素添加到页面时的事件

时间:2011-09-15 17:01:13

标签: javascript jquery dom event-handling mutation-observers

这在前面已经讨论过:How to do an action when an element is added to a page using Jquery?

每当div添加到页面时,响应者建议触发自定义事件。但是,我正在撰写Chrome扩展程序,但无法访问页面源代码。我有什么选择?我想在理论上我可以使用setTimeout来不断搜索元素的存在,并在元素存在时添加我的动作。

9 个答案:

答案 0 :(得分:81)

由于DOM Mutation事件现在是deprecated(请参阅底部的注释)并附有最新规范,并且您无法控制插入的元素,因为它们是由其他人的代码添加的,因此您唯一的选择是连续检查一下。

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

调用此函数后,它将每隔xxx毫秒运行一次。我选择了100,即1/10(十分之一秒)。除非你需要实时元素捕获,否则它就足够了。

修改

正如Ben Davis评论的那样,DOM Level 4规范引入了Mutation observers(也在Mozilla docs上),它取代了已弃用的变异事件。

答案 1 :(得分:28)

实际答案是“使用变异观察者”(如本问题中所述:Determining if a HTML element has been added to the DOM dynamically),但支持(特别是在IE上)受到限制(http://caniuse.com/mutationobserver)。

所以实际的ACTUAL答案是“最终使用变异观察者......但是现在请找Jose Faeti的答案”:)

答案 2 :(得分:16)

您还可以利用CSS3动画事件,以便在将特定元素添加到DOM时收到通知:http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/

答案 3 :(得分:13)

您可以将livequery插件用于jQuery。您可以提供选择器表达式,例如:

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

每次添加removeItemButton课程的按钮时,状态栏中都会显示一条消息。

在效率方面,您可能希望避免这种情况,但无论如何,您可以利用插件而不是创建自己的事件处理程序。

重新回答

以上答案仅用于检测项目已通过插件添加到DOM

然而,最有可能的是,jQuery.on()方法更合适,例如:

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

如果你有动态内容应该响应点击,例如,最好使用jQuery.on将事件绑定到父容器。

答案 4 :(得分:9)

ETA 24 Apr 17 我希望通过一些async / 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模式as demonstrated here

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的东西,所以要认识到兼容性问题(即,如果你的观众主要是Amish,luddite,或者更糟的是,IE8用户)

首先,我们将使用MutationObserver API来构造一个观察者对象。我们将这个对象包装在一个promise中,并在回调被触发时resolve()(h / t davidwalshblog)david walsh blog article on mutations

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>表达式时,它会以一种非阻塞的方式等待为了履行承诺( Generators比这更多,但这是我们对此感兴趣的)。

// 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突变集成到同步样式的控制流中。怎么回事呢。

答案 5 :(得分:8)

有一个很有前途的名为Arrive的javascript库,一旦浏览器支持变得司空见惯,它就像是一种开始利用变异观察者的好方法。

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

答案 6 :(得分:3)

查看此插件,该插件非常出色 - jquery.initialize

它非常像.each函数,不同之处在于它需要您输入的选择器并观察将来添加的新项目与此选择器匹配并初始化它们

Initialize看起来像这样

$(".some-element").initialize( function(){
    $(this).css("color", "blue");
});

但是现在如果新的元素匹配.some-element选择器将出现在页面上,它将被即时初始化。

添加新项目的方式并不重要,您不需要关心任何回调等。

所以,如果你要添加新的元素,如:

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

它会立即初始化。

插件基于MutationObserver

答案 7 :(得分:0)

一个纯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'));

答案 8 :(得分:0)

使用 jQuery,您可以做到 -

function nodeInserted(elementQuerySelector){
    if ($(elementQuerySelector).length===0){
        setTimeout(function(){
            nodeInserted(elementQuerySelector);
        },100);
    }else{
        $(document).trigger("nodeInserted",[elementQuerySelector]);
    }
}

该函数递归地搜索节点,直到找到它,然后针对文档触发一个事件

然后就可以用这个来实现了

nodeInserted("main");
$(document).on("nodeInserted",function(e,q){
    if (q === "main"){
        $("main").css("padding-left",0);
    }
});