MutationObserver错误:我怎么能等待元素的创建?

时间:2015-09-01 13:48:11

标签: javascript firefox greasemonkey mutation-observers

我试图制作一个Greasemonkey脚本来在Facebook页面中添加一个按钮。 但是我需要等待创建一个元素来添加按钮 我尝试使用MutationObserverAs suggested here)来执行此操作,但是出现错误。

function init() {
    console.log('Launched : ' + launched);
    if (!launched) {
        launched = true;
        main();
    }
}
console.log('barfoo');

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        if (!mutation.addedNodes) return;
        console.log(mutation.addedNodes.type());
        for (var i = 0; i < mutation.addedNodes.length; i++) {
            if (mutation.addedNodes[i].matches('div[class="pam _5shk uiBoxWhite bottomborder"]')) {
                console.log('init');
                init();
            }
        }
    })
});
console.log('bar');

try {
    observer.observe(document.body, {
        childlist: true,
        subtree: true,
        attributes: false,
        characterData: false,
    });
} catch (error) {
    console.log(error);
}
console.log('foobar');

但是我得到了以下日志:

barfoo
bar
DOMException [TypeError: "The expression cannot be converted to return the specified type."
code: 0
nsresult: 0x805b0034
location: file:///home/vmonteco/.mozilla/firefox/f3xgmo8e.default/gm_scripts/Facebook_cleaner/Facebook_cleaner.user.js:230] Facebook_cleaner.user.js:239:3
foobar

此错误的原因是什么? 我怎么能解决这个问题?

如果在执行我的脚本之前创建了元素,MutationObserver是否仍会被触发? (IE,如果在创建MutationObserver时元素已经存在)。

NB:我尝试使用$(document).ready(),但在创建要添加按钮的元素之前触发了它。

NB-bis :我没有发布整个剧本。

3 个答案:

答案 0 :(得分:6)

遗憾的是,该错误信息完全含糊不清,但您有一个错字:

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

应该是

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

e.g。 childlist =&gt; childList

在这种情况下,在Chrome中运行相同的代码会提供比#34更有用的消息;无法转换为返回指定的类型&#34;。它说

  

选项对象必须至少设置一个属性&#39;,&#39; characterData&#39;或&#39; childList&#39;为真。

答案 1 :(得分:1)

实际上,MutationObserver这样做的最好方法(正如你正在努力学习的那样。它很快变得复杂和脆弱。它可能真的陷入网页,并且它在各个平台之间并不一致。) 此外,如果在脚本触发之前元素存在,MutationObserver将无法捕获它。

使用Run Greasemonkey script on the same page, multiple times?中显示的方法。

这样,您的整个脚本就变为:

// ==UserScript==
// @name     _Wait for specific divs
// @include  http://YOUR_SERVER.COM/YOUR_PATH/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant    GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
    introduced in GM 1.0.   It restores the sandbox.
*/
waitForKeyElements (
    'div.pam _5shk.uiBoxWhite.bottomborder', processDiv
);

function processDiv (jNode) {
    //  YOUR CODE HERE.
    // For example:
    jNode.css ("background", "pink");
}

看看这有多容易! :)
使用waitForKeyElements()您可以根据需要使用任何复杂的jQuery选择器。

如果你真的坚持MutationObserver,请使用一个可以消除很多痛苦的库。
This answer shows how to use MutationObserver in a Greasemonkey/Tampermonkey script.

答案 2 :(得分:-1)

我认为 MutationObserver 并非实现此目的的方法,但实际上是这样。细微之处在于,如果您要处理从一开始就立即加载的页面节点,那么为了不丢失任何内容,您必须使用// @run-at document-start条件运行Monkey脚本,然后附加观察者,例如到document.documentElement以捕获绝对的所有内容(或者,如果您确定以后页面中的所有内容都不会改变而将其中断,则返回到以后出现的元素):

(function()
{
  new MutationObserver((mutationsList, observer) => { 
    for (let mutation of mutationsList)
    {
      if (mutation.type != 'childList' || !mutation.addedNodes.length)
      {
        continue;
      } 
      Array.from(mutation.addedNodes).forEach(function(e) // or forSome if you only want to catch one object such as a button
      {
        if (!(e instanceof Element))
        {
          return;
        }
        // do stuff
      });  
    }
  }).observe(document.documentElement, {childList: true, subtree: true});
})();