Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
157 views
in Technique[技术] by (71.8m points)

javascript - Watch for element creation in greasemonkey script?

I need to be notified when an element with class 'nav' is created as the document is loading. Googling I found MutationObservers and thought they would be perfect, but I can't seem to get it working.

// ==UserScript==
// @name        ii-shortcuts
// @namespace   https://github.com/RedHatter
// @include     *
// @version     1
// @run-at document-start
// ==/UserScript==

var observer = new MutationObserver(function(mutations)
{
    mutations.forEach(function(mutation)
    {
        if (mutation.target.getAttribute('class') == 'nav')
            GM_log('nav creation');
    });    
});
observer.observe(document, {subtree: true, attributes: true, attributeFilter: ['class']});    

I also tried.

// ==UserScript==
// @name        ii-shortcuts
// @namespace   https://github.com/RedHatter
// @include     *
// @version     1
// @run-at document-start
// ==/UserScript==

var observer = new MutationObserver(function(mutations)
{
    mutations.forEach(function(mutation)
    {
        if (mutation.addedNodes[0].getAttribute('class') == 'nav')
            GM_log('nav creation');
    });    
});
observer.observe(document, {subtree: true, childList: true});

But in nether case was 'nav creation' log on page load. What am I missing?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Several issues (big to small):

  1. When the document is first, statically loaded; the events are childList events, not attributes events.

    For example,

    $("body").append ('<p id="foo" class="bar">Hiya!</p><p>blah</p>');
    

    generates one childList event, while a subsequent

    $("#foo").attr ("class", "bar2");
    

    generates an attributes event.

  2. The odds that mutation.addedNodes[0] contains an element with class nav are practically zero. This is almost always a text node.
    You need to check the whole array, PLUS the target.

  3. Don't use getAttribute('class') == 'nav' to check for classes. This will throw exceptions for nodes without the getAttribute function and it will miss elements that have more than one class. EG: <p class="foo nav bar">...

    Use classList.contains() on appropriate node types.

  4. Use a @grant directive if you use any GM_ functions like GM_log(). Use a grant anyway, to ensure that the sandbox stays on.

  5. Avoid the use of // @include *. Especially with timers and observers, this can bog down your browser and your machine.

  6. This info is for Firefox. Chrome has big differences in how it implements Mutation observers. This kind of code will not work in Chrome before page load.


Putting it all together, the script becomes:

// ==UserScript==
// @name        _ii-shortcuts
// @namespace   https://github.com/RedHatter
// @include     http://YOUR_SERVER.COM/YOUR_PATH/*
// @run-at      document-start
// @version     1
// @grant       GM_log
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
    introduced in GM 1.0.   It restores the sandbox.
*/
var MutationObserver = window.MutationObserver;
var myObserver       = new MutationObserver (mutationHandler);
var obsConfig        = {
    childList: true, attributes: true,
    subtree: true,   attributeFilter: ['class']
};

myObserver.observe (document, obsConfig);

function mutationHandler (mutationRecords) {

    mutationRecords.forEach ( function (mutation) {

        if (    mutation.type               == "childList"
            &&  typeof mutation.addedNodes  == "object"
            &&  mutation.addedNodes.length
        ) {
            for (var J = 0, L = mutation.addedNodes.length;  J < L;  ++J) {
                checkForCSS_Class (mutation.addedNodes[J], "nav");
            }
        }
        else if (mutation.type == "attributes") {
            checkForCSS_Class (mutation.target, "nav");
        }
    } );
}

function checkForCSS_Class (node, className) {
    //-- Only process element nodes
    if (node.nodeType === 1) {
        if (node.classList.contains (className) ) {
            console.log (
                'New node with class "' + className + '" = ', node
            );
            // YOUR CODE HERE
            //GM_log ('nav creation');
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...