Pregunta

Quiero que todos los botones realicen una acción antes y después de su evento onclick normal. Así que se me ocurrió el " brillante " idea de recorrer todos esos elementos y crear una función de envoltura.

Esto pareció funcionar bastante bien cuando lo probé, pero cuando lo integré en nuestra aplicación, se derrumbó. Lo rastreé hasta que el valor de "este" fue cambiado por mi envoltorio. El código de ejemplo ilustra esto; antes de envolver los controladores de eventos, cada botón muestra la identificación del botón cuando hace clic, pero después de envolverlo, el nombre que se muestra es "indefinido" en este ejemplo, o "Form1" si lo ejecuta desde un formulario.

¿Alguien sabe una mejor manera de hacer lo mismo? ¿O es una buena manera de mantener los valores de 'este' originalmente pensados?

Como puede imaginar, no quiero modificar ninguno de los códigos existentes del controlador de eventos en los botones de destino.

Gracias de antemano.

PS-El navegador de destino es IE6 & amp; arriba, no se requiere la funcionalidad de cursor cruzado

<!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>
¿Fue útil?

Solución

Al igual que Paul Dixon , puedes usar call pero le sugiero que use apply en su lugar.

Sin embargo, la razón por la que estoy respondiendo es que encontré un error molesto: En realidad, estás reemplazando todos tus controladores de eventos con el controlador de eventos del último botón. No creo que fuera eso. lo pretendías, ¿verdad? (Sugerencia: está reemplazando el valor de originalEventHandler en cada iteración)

En el código a continuación encontrará una solución de navegador cruzado que funciona:

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

Primero creo una nueva función anónima y la almaceno en la variable newOnClick . Como una función es un objeto, puedo crear propiedades en el objeto de función como cualquier otro objeto. Lo uso para crear la propiedad original que es el controlador original de clics, y source que es el elemento fuente que será el este cuando Se llama el controlador original.

Dentro de la función anónima, necesito obtener una referencia a la función para poder obtener el valor de las propiedades original y source . Dado que la función anónima no tiene un nombre, uso argument.callee (que ha sido compatible desde MSIE5.5) para obtener esa referencia y almacenarla en la variable src.

Luego uso el método apply para ejecutar el controlador original de onclick. apply toma dos parámetros: el primero será el valor de this , y el segundo es una serie de argumentos. este tiene que ser el elemento al que se adjuntó el controlador original onclick, y ese valor se guardó en source . argumentos es una propiedad interna de todas las funciones y contiene todos los argumentos con los que se llamó a la función (observe que la función anónima no tiene ningún parámetro especificado, pero si se llama con algunos parámetros de todos modos, se encontrará en la propiedad argumentos ).

La razón por la que uso aplicar es que puedo reenviar todos los argumentos con los que se llamó a la función anónima, y ??esto hace que esta función sea transparente y de navegador cruzado. (Microsoft puso el evento en window.event pero los otros navegadores lo proporcionan en el primer parámetro de la llamada del controlador)

Otros consejos

Puede utilizar el método call para resolver el vinculante, por ejemplo originalEventHandler.call(btn);

Alternativamente, una biblioteca como prototipo puede ayudar: su método bind le permite compilar una nueva función vinculada a un objeto especificado, por lo que habría declarado originalEventHandler como var originalEventHandler = btn.onclick.bind (btn);

Por último, para un buen antecedente sobre temas vinculantes, vea también Cómo salir de las situaciones de enlace en JavaScript

Su problema es la forma en que funcionan los cierres en JavaScript. Sinceramente, recomiendo usar un marco. Cualquiera de ellos debería hacer que el manejo de eventos sea mucho más agradable que hacerlo a mano.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top