Domanda

Voglio che tutti i pulsanti eseguano un'azione prima e dopo il loro normale evento onclick. Quindi ho pensato al "brillante" idea di fare il ciclo tra tutti quegli elementi e creare una funzione wrapper.

Questo sembra funzionare abbastanza bene quando l'ho provato, ma quando l'ho integrato nella nostra app, è andato in pezzi. L'ho rintracciato fino al valore 'this' è stato modificato dal mio wrapper. Il codice di esempio illustra questo; prima di racchiudere i gestori di eventi, ogni pulsante visualizza l'id pulsante quando si fa clic, ma dopo averlo racchiuso il nome visualizzato è "non definito" in questo esempio o "Form1" se lo si esegue da un modulo.

Qualcuno conosce un modo migliore per fare la stessa cosa? O un buon modo per mantenere i valori "questo" inizialmente previsti?

Come puoi immaginare, non voglio modificare nessuno dei codici del gestore eventi esistenti nei pulsanti di destinazione.

Grazie in anticipo.

PS-Il browser di destinazione è IE6 & amp; su, funzionalità crossbrowser non richiesta

<!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>
È stato utile?

Soluzione

Come diceva Paul Dixon , puoi usare call ma ti suggerisco di usare applica invece.

Tuttavia, il motivo per cui sto rispondendo è che ho trovato un bug inquietante: In realtà stai sostituendo tutti i gestori di eventi con il gestore di eventi dell'ultimo pulsante. Non credo che sia stato quello intendevi, vero? (Suggerimento: stai sostituendo il valore per originalEventHandler in ogni iterazione)

Nel codice sottostante trovi una soluzione cross-browser funzionante:

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

Per prima cosa creo una nuova funzione anonima e la memorizzo nella variabile newOnClick . Poiché una funzione è un oggetto, posso creare proprietà sull'oggetto funzione come qualsiasi altro oggetto. Lo uso per creare la proprietà originale che è il gestore onclick originale e sorgente che è l'elemento sorgente che sarà questo quando viene chiamato il gestore originale.

All'interno della funzione anonima ho bisogno di ottenere un riferimento alla funzione per poter ottenere il valore delle proprietà originale e sorgente . Poiché la funzione anonima non ha un nome che uso, usa argument.callee (supportato da MSIE5.5) per ottenere quel riferimento e memorizzarlo in src variabile.

Quindi uso il metodo applica per eseguire il gestore onclick originale. applica accetta due parametri: il primo sarà il valore di questo e il secondo è un array di argomenti. questo deve essere l'elemento al quale è stato associato il gestore onclick originale e quel valore è stato salvato in sorgente . argomenti è una proprietà interna di tutte le funzioni e contiene tutti gli argomenti con cui è stata chiamata la funzione (si noti che la funzione anonima non ha alcun parametro specificato, ma se viene comunque chiamata con alcuni parametri, essi sarà trovato nella proprietà argomenti ).

Il motivo per cui utilizzo applica è che posso inoltrare tutti gli argomenti con cui è stata chiamata la funzione anonima, e questo rende questa funzione trasparente e cross-browser. (Microsoft inserisce l'evento in window.event ma gli altri browser lo forniscono nel primo parametro della chiamata del gestore)

Altri suggerimenti

Puoi utilizzare il metodo call vincolante, ad es originalEventHandler.call (BTN);

In alternativa, una libreria come il prototipo può aiutare: il suo metodo bind ti consente di creare una nuova funzione associata a un oggetto specificato, quindi avresti dichiarato originalEventHandler come var originalEventHandler = btn.onclick.bind (btn);

Infine, per un buon backgrounder su problemi di rilegatura, vedi anche Come uscire da situazioni vincolanti in JavaScript

Il tuo problema è il modo in cui le chiusure funzionano in JavaScript. Onestamente, consiglierei di usare un framework. Ognuno di loro dovrebbe rendere la gestione degli eventi molto più piacevole di farlo manualmente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top