1

すべてのボタンが通常の onclick イベントの前後にアクションを実行するようにします。そこで、これらすべての要素をループしてラッパー関数を作成するという「素晴らしい」アイデアを思いつきました。

これは、テストしたときはうまく機能しているように見えましたが、アプリに統合するとうまくいきませんでした。「this」の値がラッパーによって変更されたことをたどりました。サンプル コードはこれを示しています。イベント ハンドラーをラップする前は、クリックすると各ボタンにボタン ID が表示されますが、ラップ後に表示される名前は、この例では「未定義」、フォーム内から実行した場合は「Form1」です。

同じことを行うためのより良い方法を知っている人はいますか? または、当初意図されていた「この」値を維持するための良い方法はありますか?

ご想像のとおり、ターゲット ボタンの既存のイベント ハンドラー コードを変更したくありません。

前もって感謝します。

PS - ターゲット ブラウザは IE6 以降で、クロスブラウザ機能は必要ありません

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<script language="javascript" type="text/javascript">
    function btnWrap_onClick()
    {
        var btns = document.getElementsByTagName("button");
        for( var i = 0; i < btns.length; i++)
        {
            var btn = btns[i];

            // handle wrap button differerntly
            if( "btnWrap" == btn.id)
            {
                btn.disabled = true;
                continue; // skip this button
            }

            // wrap it
            var originalEventHandler = btn.onclick;
            btn.onclick = function()
            {
                alert("Starting event handler");
                originalEventHandler();
                alert("Finished event handler");
            }
        }

        alert("Buttons wrapped successfully");
    }
</script>
<body>
    <p>
    <button id="TestButton1" onclick="alert(this.id);">TestButton1</button>
    <button id="TestButton2" onclick="alert(this.id);">TestButton2</button>
    </p>
    <button id="btnWrap" onclick="btnWrap_onClick();">Wrap Event Handlers</button>
</body>
</html>
4

3 に答える 3

4

callメソッドを使用してバインディングを解決できます。originalEventHandler.call(btn);

別の方法として、prototype のようなライブラリが役立ちます。そのbindメソッドを使用すると、指定したオブジェクトにバインドされた新しい関数を作成できます。var originalEventHandler = btn.onclick.bind(btn);

最後に、バインドの問題に関するバックグラウンダーについては、Getting Out of Binding Situations in JavaScriptも参照してください。

于 2008-12-30T17:59:45.673 に答える
3

Paul Dixonが言ったように、call使用できますが、代わりにapplyを使用することをお勧めします。

ただし、私が回答している理由は、気がかりなバグを見つけたからです。実際には、すべてのイベント ハンドラーを最後のボタンのイベント ハンドラーに置き換えています。それはあなたが意図したものではないと思いますよね?(ヒント: 各反復で originalEventHandler の値を置き換えています)

以下のコードでは、動作するクロスブラウザー ソリューションを見つけます。

function btnWrap_onClick()
{
    var btns = document.getElementsByTagName("button");
    for( var i = 0; i < btns.length; i++)
    {
        var btn = btns[i];

        // handle wrap button differerntly
        if( "btnWrap" == btn.id)
        {
            btn.disabled = true;
            continue; // skip this button
        }

        // wrap it

        var newOnClick = function()
        {
            alert("Starting event handler");
            var src=arguments.callee;
            src.original.apply(src.source,arguments);
            alert("Finished event handler");
        }
        newOnClick.original = btn.onclick; // Save original onClick
        newOnClick.source = btn; // Save source for "this"
        btn.onclick = newOnClick; //Assign new handler
    }
alert("Buttons wrapped successfully");
}

まず、新しい無名関数を作成し、それを変数newOnClickに格納します。関数はオブジェクトなので、他のオブジェクトと同じように関数オブジェクトにプロパティを作成できます。これを使用して、元の onclick-handler である original プロパティと元のハンドラーが呼び出されたときにthisになるソース要素である sourceを作成します。

匿名関数内では、プロパティoriginalおよびsourceの値を取得できるように、関数への参照を取得する必要があります。無名関数には名前がないため、arguments.callee (MSIE5.5 以降でサポートされています) を使用してその参照を取得し、変数 src に格納します。

次に、applyメソッドを使用して、元の onclick ハンドラーを実行します。applyは 2 つのパラメーターを取ります。1 つ目はthisの値になり、2 つ目は引数の配列になります。これは、元の onclick ハンドラーがアタッチされた要素である必要があり、その値はsourceに保存されました。引数はすべての関数の内部プロパティであり、関数が呼び出されたすべての引数を保持します (匿名関数にはパラメーターが指定されていないことに注意してください。)。

applyを使用する理由は、無名関数の呼び出しに使用されたすべての引数を転送できるためです。これにより、この関数が透過的でクロスブラウザーになります。(Microsoft はイベントを window.event に配置しますが、他のブラウザーはハンドラー呼び出しの最初のパラメーターで提供します)

于 2008-12-30T22:07:40.697 に答える
1

あなたの問題は、JavaScript でクロージャーが機能する方法です。正直なところ、フレームワークを使用することをお勧めします。それらのいずれも、イベント処理を手動で行うよりもはるかに優れたものにするはずです。

于 2008-12-30T17:55:41.983 に答える