2012-02-08 10 views
2

私はページにいくつかの機能を実行するブックマークレットを書いています。この機能の一部として、getElementsByClassNameを使用する必要があります。しかし、テスト中にいくつかのWebサイトがカスタムメソッドにgetElementsByClassNameを再定義していることがわかりました。これはすべてのブラウザでgetElementsByClassNameをサポートするために行われたと思われます。再定義された関数の元の定義を取得する

カスタムgetElementsByClassNameの実装はちょっとうんざりしていて、いくつかの使用例では失敗します。 getElementsByClassNameの元の定義を得る方法はありますか?

Chromeのjavascriptコンソールで:getElementsByClassNameがネイティブ関数を指しています。 getElementsByClassNameが再定義されたので、このネイティブ関数にアクセスする方法はありますか?

+0

事前にキャッシュすることも、ネイティブサポートが存在しない場合(ほとんどの場合はこれを行う必要があります)にのみサポートを追加するようにスクリプトを変更することはできますが、一度オーバーライドすると元に戻すことはできません。 – davin

+0

@davin残念ながら、私はページやそのスクリプトを所有/制御していません。 – asleepysamurai

+1

私が言ったように、私は、ページに埋め込まれているスクリプトではなく、ブックマークレットを書いています。だから私はgetElementsByClassNameをredineする関数を変更することができません。なぜなら、私はそれにアクセスする他の方法を探しているのです。 – asleepysamurai

答えて

3

解決策はありますが、堅牢ではありません。下の免責条項を参照してください。

新しいウィンドウを開くと、変更されていないメソッドにアクセスできます。新しいウィンドウを開くには、window.openを使用し、iframeに2つの方法があります。 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がQuirksモードまたは(あなたがブックマークレットでほとんど制御を超える持っている)バージョン9以前のgetElementsByClassNameを好きではないことを除いて、ほとんどクロスブラウザです。ここにjsfiddleです:http://jsfiddle.net/theazureshadow/vdqYG/

そして、あなたは他の方法をインポートすると私の実験について興味があれば、このjsfiddleを見てみましょう:http://jsfiddle.net/theazureshadow/ccFG3/

免責事項:現在のコンテキスト内で他のウィンドウからメソッドを使用して危険な、未定義の領域です。この脆弱なアプローチを使用する場合は、ブラウザ間の互換性をテストしてください。私のテストでは、さまざまなブラウザがこのアプローチをどのように扱っているかに大きな違いが見られました。本番サイトでは使用しないでくださいが、個人用のブックマークレットでも問題ありません。使用したいメソッドの独自のバージョンを含めることはおそらくもっと安全です。

2

私はできないと思います。これが、なぜMonkeypatchingが悪いのかです。ホストオブジェクトを決して再定義するべきではありません。

0

カスタムメソッドの定義方法によって異なります。このメソッドにはElement.prototype.getElementsByClassNameという名前でアクセスできますが、このプロトタイプメソッドをページが上書きすると、戻す方法はないと思います。

1

プロトタイプを使用するサイトがあります。 DOMのdocument.getElementsByClassName()NodeListを返しますが、Prototypeのdocument.getElementsByClassName()NodeListをJSスクリプトで作成できないため、Arrayを返します。

Firefoxでは、あなたは、元のメソッドを取得するために

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

を使用することができます。たぶん、Google Chromeは似たようなものを実装しています。私は何も見つかりませんでした(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 Google Chromeが実装している場合:

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; 
} 

この機能を指定されたノード内のすべての要素を反復処理します。プロトタイプのような配列を返し、同じ欠点を共有します。配列はNodeListのように「ライブ」ではありません。

+0

私はあなたの2番目の解決策に似た何かをやりました。私はNodeListが必要なので、3番目の解決策は私のために実際には機能しません。現在、FirefoxのlookupMethodのためのWebkit同等のメソッドがあるかどうかを調べようとしています。ありがとう! – asleepysamurai

関連する問題