Est-il possible d'empêcher "cela" de changer lorsque j'emballe une fonction?

StackOverflow https://stackoverflow.com/questions/400968

  •  03-07-2019
  •  | 
  •  

Question

Je veux que tous les boutons effectuent une action avant et après leur événement onclick normal. Alors je suis venu avec le "brillant" idée de boucler tous ces éléments et de créer une fonction wrapper.

Cela semblait bien fonctionner lorsque je l'ai testé, mais lorsque je l'ai intégré à notre application, il s'est effondré. Je l'ai tracé jusqu'à la valeur 'this' a été modifiée par mon wrapper. L'exemple de code illustre cela. Avant d'encapsuler les gestionnaires d'événements, chaque bouton affiche son identifiant lorsque vous cliquez dessus. Toutefois, après cet emballage, le nom affiché est "non défini" dans cet exemple, ou "Form1" si vous l'exécutez à partir d'un formulaire.

Quelqu'un connaît-il une meilleure façon de faire la même chose? Ou un bon moyen de maintenir les valeurs "ceci" initialement prévues?

Comme vous pouvez l’imaginer, je ne souhaite modifier aucun des codes de gestionnaire d’événements existants dans les boutons cible.

Merci d'avance.

PS-Le navigateur cible est IE6 & amp; up, fonctionnalité crossbrowser non requise

<!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>
Était-ce utile?

La solution

Comme Paul Dixon , vous pouvez utiliser appel mais je suggère d'utiliser s’applique à la place.

Cependant, la raison pour laquelle je réponds, c'est que j'ai trouvé un bogue inquiétant: vous remplacez en fait tous vos gestionnaires d'événements par le gestionnaire d'événements du dernier bouton. Je ne pense pas que ce soit ce vous vouliez, était-ce? (Conseil: vous remplacez la valeur de originalEventHandler à chaque itération)

Dans le code ci-dessous, vous trouverez une solution multi-navigateur qui fonctionne:

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

Tout d'abord, je crée une nouvelle fonction anonyme et la stocke dans la variable newOnClick . Puisqu'une fonction est un objet, je peux créer des propriétés sur l'objet fonction comme n'importe quel autre objet. J'utilise ceci pour créer la propriété originale qui est le gestionnaire onclick d'origine, et source qui est l'élément source qui sera l'élément this lorsque le gestionnaire d'origine est appelé.

Dans la fonction anonyme, je dois obtenir une référence à la fonction pour pouvoir obtenir la valeur des propriétés originale et source . La fonction anonyme n'ayant pas de nom, j'utilise arguments.callee (pris en charge depuis MSIE5.5) pour obtenir cette référence et la stocker dans la variable src.

J'utilise ensuite la méthode apply pour exécuter le gestionnaire onclick d'origine. apply prend deux paramètres: le premier va être la valeur de this , et le second est un tableau d'arguments. this doit être l'élément auquel le gestionnaire onclick d'origine a été attaché et cette valeur a été enregistrée dans source . arguments est une propriété interne de toutes les fonctions et contient tous les arguments avec lesquels la fonction a été appelée (notez que la fonction anonyme n'a aucun paramètre spécifié, mais si elle est appelée avec certains paramètres, sera trouvé dans la propriété arguments ).

La raison pour laquelle j'utilise apply est que je peux transférer tous les arguments avec lesquels la fonction anonyme a été appelée, ce qui rend cette fonction transparente et multi-navigateurs. (Microsoft a placé l’événement dans window.event mais les autres navigateurs l’a fourni dans le premier paramètre de l’appel du gestionnaire)

Autres conseils

Vous pouvez utiliser la méthode appeler pour résoudre le problème. contraignant, par exemple originalEventHandler.call (btn);

Une bibliothèque comme un prototype peut également aider - sa méthode bind permet de créer une nouvelle fonction liée à un objet spécifié, de sorte que vous auriez déclaré originalEventHandler comme var originalEventHandler = btn.onclick.bind (btn);

Enfin, pour une bonne documentation sur les problèmes de liaison, voir aussi Comment sortir de la situation contraignante en JavaScript

Votre problème est la façon dont les fermetures fonctionnent en JavaScript. Honnêtement, je recommanderais d'utiliser un cadre. N'importe lequel d'entre eux devrait rendre la gestion des événements bien plus agréable que de le faire à la main.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top