代码用作Opera UserJS,但在Chrome和Greasemonkey用户脚本中出现“未定义”错误

时间:2013-01-29 14:12:45

标签: javascript cross-browser greasemonkey userscripts userjs

我正在尝试编写一个隐藏IMDb评级的跨浏览器用户脚本,并将其替换为允许用户稍后对该节目/电影进行评级的链接。

在Opera中,这种方法已经有效,但Firefox和Chrome都发现了一个未定义的函数和/或变量。这是我目前的代码:

function hideimdbratings() {

    if (document.getElementsByClassName("star-box")[0]) {

        reenabled = document.getElementsByClassName("star-box")[0].innerHTML;

        document.getElementsByClassName("star-box")[0].innerHTML = '<a href="#" id="showvote" onclick="reenableit();">Rate!</a>';

    }
}

function reenableit() {
    document.getElementsByClassName("star-box")[0].innerHTML = reenabled;
}

window.addEventListener('load', hideimdbratings, false);

第一部分工作正常,hideimdbratings()被执行并隐藏评级。

但是,点击“评价!”链接后,Firefox和Chrome都会说reenableit()未定义。

试试这个:

onclick="document.getElementsByClassName(\"star-box\")[0].innerHTML = reenabled;"

结果是reenabled未定义。

我尝试将代码直接放入事件监听器:

window.addEventListener("load", function(e) {
// ...
}, false);

这种定义函数的方法(使用Firefox的unsafeWindow):

var window.reenableit = function() { }

但是,无论我做什么,reenableit()reenabled都未定义。根据我的理解,除了Opera之外,函数和变量都不是全局的,但我似乎还没有找到解决方案。

我错过了什么/做错了什么?

3 个答案:

答案 0 :(得分:1)

我无法弄清楚如何使变量或函数全局化,虽然这是一个解决方案应该适合你:

function hideimdbratings() {
    if (document.getElementsByClassName('star-box')[0]) {

        // hide the "star-box" container
        var b = document.getElementsByClassName('star-box')[0];
        b.style.display = 'none';

        // add the "Rate!" link
        var n = document.createElement('a');
        n.setAttribute('href', 'javascript:void(0);');
        n.setAttribute('onclick', 'document.getElementsByClassName("star-box")[0].style.display = "block"; this.style.display = "none";');
        n.innerHTML = 'Rate!';
        b.parentNode.insertBefore(n, b.nextSibling);
    }
}

window.addEventListener('load', hideimdbratings, false);

答案 1 :(得分:1)

该代码无法在Chrome中使用,因为Chrome用户脚本在沙箱中运行("isolated world"),您无法在Chrome中设置或使用页面范围的javascript对象。而且,Chrome无法完全/正确支持unsafeWindow

代码在Firefox + Greasemonkey中工作并谨慎使用unsafeWindow,但这里不建议这样做(并且对Chrome没有帮助)。

经典方法,当需要以跨浏览器方式与页面范围javascript交互时,使用Script Injection。这是唯一适用于Chrome的功能。

然而,最明智的做法就是不使用页面范围JS,如果你不需要的话。并且,对于这个问题中的内容,您不需要。 (提示:永远不要使用onclick或类似属性!始终使用addEventListener()或同等属性。)

重构代码以避免离开沙箱范围,它变为:

function hideImdbRatings () {
    var oldStarBoxHTML;
    var starBox = document.getElementsByClassName ("star-box");
    if (starBox.length) {
        starBox             = starBox[0];
        oldStarBoxHTML      = starBox.innerHTML;
        starBox.innerHTML   = '<a href="#" id="showVote">Rate!</a>';

        document.getElementById ("showVote").addEventListener (
            "click",
            function () {
                //-- "this" is a special javascript scope.
                this.innerHTML = oldStarBoxHTML;
            }
        );
    }
}

window.addEventListener ('load', hideImdbRatings, false);


但是2 ,您会注意到目前为止 所有 代码,破坏了IMDB评级小部件的互动。这是因为它覆盖了innerHTML,它会破坏小部件的事件处理程序。不要那样使用innerHTML

最明智的做法是隐藏块,类似于Geo的回答,如下:

 function hideImdbRatings () {
    var starBox = document.getElementsByClassName ("star-box");
    if (starBox.length) {
        starBox                 = starBox[0];
        starBox.style.display   = 'none';
        var rateLink            = document.createElement ('a');
        rateLink.id             = 'showVote';
        rateLink.href           = '#';
        rateLink.textContent    = 'Rate!';

        starBox.parentNode.insertBefore (rateLink, starBox);

        document.getElementById ("showVote").addEventListener (
            "click",
            function () {
                //-- "this" is a special javascript scope.
                this.style.display      = 'none';
                starBox.style.display   = 'block';
            }
        );
    }
}

window.addEventListener ('load', hideImdbRatings, false);


还有可能会阻止脚本在Chrome中运行的其他因素。 By default, Chrome userscripts may run after the load event.要解决此问题,请在脚本the metadata block中指定@run-at document-end

答案 2 :(得分:0)

啊哈!找到了。试试这个:

var uw = (this.unsafeWindow) ? this.unsafeWindow : window;
var b = document.getElementsByClassName('star-box')[0];
uw.reenabled = b.innerHTML;
b.innerHTML = '<a href="javascript:void(0);" onclick="document.getElementsByClassName(\'star-box\')[0].innerHTML = reenabled;">Rate!</a>';

this post启发