2012-02-08 95 views
2

我正在寫一個書籤,它在頁面上執行一些功能。作爲這個功能的一部分,我需要使用getElementsByClassName。但是,在測試過程中,我發現幾個網站已將getElementsByClassName重新定義爲自定義方法。推測這是爲了支持所有瀏覽器中的getElementsByClassName獲取重新定義函數的原始定義

自定義getElementsByClassName的實現有點草率,並且在我的幾個用例中失敗。有什麼辦法可以得到getElementsByClassName的原始定義?

在鉻的JavaScript控制檯:getElementsByClassName指向本機功能。有沒有辦法訪問這個本地函數,現在getElementsByClassName已被重新定義?

+0

可以預先緩存,或者更改這些腳本添加支持,只有當原生支持不存在(最應該做到這一點無論如何),但一旦它的覆蓋,你不能拿回來。 – davin 2012-02-08 13:22:33

+0

@達文不幸的是,我不擁有/控制頁面或腳本。 – asleepysamurai 2012-02-08 13:25:40

+1

就像我說的,我寫一個書籤,而不是已嵌入到網頁中的腳本。所以我不能修改其redines getElementsByClassName方法,這就是爲什麼我要尋找訪問它的其他方式的功能。 – asleepysamurai 2012-02-08 13:31:08

回答

3

有一種解決方案,但它不健壯。請查看底部的免責聲明。

如果您打開一個新窗口,您將有權訪問其未修改的方法。我可以想出兩種方法來打開一個新窗口:使用window.open,並在iframe中。 iframe不那麼突兀,因爲它不會通過打開新的瀏覽器窗口或觸發彈出窗口攔截器來分散用戶的注意力。

// Runs fn with a clean window reference 
function getCleanReferences(fn) { 
    var iframe = document.createElement('iframe'); 
    iframe.style.display = 'none'; 
    document.body.appendChild(iframe); 
    var newWindow = iframe.contentWindow; 
    iframe.parentNode.removeChild(iframe); 
    return fn(newWindow); 
} 

某些瀏覽器不喜歡刪除iframe。如果您希望在Safari或Firefox中使用此工具,請刪除removeChild行。接下來,您將希望從新窗口獲取getElementsByClassName方法。

var nativeGetElementsByClassName = getCleanReferences(function(newWindow) { 
    var getElementsByClassName = newWindow.document.getElementsByClassName; 
    return function(className) { 
     return getElementsByClassName.call(window.document, className); 
    }; 
}); 

這是相當多的跨瀏覽器,但IE不喜歡getElementsByClassName方法怪癖模式或版本9(你的書籤幾乎沒有控制權)前。這裏有一個的jsfiddle:http://jsfiddle.net/theazureshadow/vdqYG/

如果您想了解我的實驗與導入等方法,看看這的jsfiddle:http://jsfiddle.net/theazureshadow/ccFG3/

免責聲明:從其他窗口在當前的背景下使用方法一個是危險的,未定義的領土。如果您選擇使用這種易碎的方法,請確保您測試跨瀏覽器的兼容性。我的測試顯示了不同瀏覽器對待此方法的主要差異。你不應該在生產網站上使用它,但它可能是一個個人書籤的好處。只要包含您想要使用的任何方法的自己版本,可能會更安全。

2

我認爲這是不可能的。這就是monkeypatching不好的原因。一個人不應該重新定義主機對象。

0

這取決於如何定義自定義方法。你可以訪問方法Element.prototype.getElementsByClassName,但如果頁面覆蓋這個原型方法,我不認爲有辦法讓它恢復。

1

我想有些網站使用原型。 DOM的document.getElementsByClassName()返回NodeList,而原型的document.getElementsByClassName()返回Array,因爲NodeList s不能由JS腳本創建。

在Firefox中,你可以使用

var node = document; 
Components.lookupMethod(node, 'getElementsByClassName').call(node, /* className */); 

得到原始方法。也許谷歌瀏覽器實現類似的東西,否則你會失敗。我找不到任何東西(2分鐘谷歌搜索)。

在這種情況下,你可以使用這樣的事情:

function getElementsByClassName(node, className) { 
    var rv = new Array(); 
    var nodeList = node.getElementsByTagName('*'); 
    className = className.replace(/([\.\\\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-])/, '\\$1'); 
    var regex = new RegExp('(?:^|[\\n\\r ])' + className + '(?:[\\n\\r ]|$)'); 
    for(var length = nodeList.length, i = 0; i < length; ++i) { 
     if(regex.test(nodeList[ i ].className)) { 
      rv.push(nodeList[ i ]); 
     } 
    } 
    return rv; 
} 

如果谷歌瀏覽器實現node.classList您可以使用下面的函數,它不使用正則表達式令人毛骨悚然:

function getElementsByClassName(node, className) { 
    var rv = new Array(); 
    var nodeList = node.getElementsByTagName('*'); 
    for(var length = nodeList.length, i = 0; i < length; ++i) { 
     if(nodeList[ i ].classList.contains(className)) { 
      rv.push(nodeList[ i ]); 
     } 
    } 
    return rv; 
} 

此功能迭代給定節點內的所有元素。它返回一個像Prototype這樣的數組,並共享相同的缺點:數組不像「活」,如NodeList s。

+0

我最終做了類似於第二種解決方案的事情。第三種解決方案並不適合我,因爲我需要一個NodeList。目前正在嘗試查找是否存在用於Firefox的lookupMethod的webkit等效方法。謝謝! – asleepysamurai 2012-02-08 16:44:02