【问题标题】:Code works as an Opera UserJS, but gives "undefined" errors in Chrome and Greasemonkey userscripts代码作为 Opera UserJS 工作,但在 Chrome 和 Greasemonkey 用户脚本中出现“未定义”错误
【发布时间】:2013-01-29 14:12:44
【问题描述】:

我正在尝试编写一个跨浏览器用户脚本来隐藏 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() 被执行并隐藏评级。

但是,点击“Rate!”链接后,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 以外的浏览器中都不是全局的,但我似乎还没有找到解决方案。

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

【问题讨论】:

    标签: javascript cross-browser greasemonkey userscripts userjs


    【解决方案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

    【讨论】:

    • @BrockAdams 好样的!最聪明的 :-)
    • @BrockAdams 是的,我的错。点击。
    • @mihau,不错。只需确保您知道,因为您是 Stack Overflow 的新手。
    【解决方案2】:

    我不知道如何使变量或函数成为全局变量,但这里有一个解决方法,应该对你很有效:

    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。但不要使用onclickespecially in userscripts,避免使用innerHTML AMAP。
    【解决方案3】:

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-28
      • 2023-04-08
      • 1970-01-01
      • 2013-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多