La valeur de « ce » dans le gestionnaire en utilisant addEventListener

voix
41

J'ai créé un objet javascript via le prototypage. Je suis en train de rendre une table dynamique. Alors que la partie de rendu est simple et fonctionne très bien, je dois aussi gérer certains événements côté client pour la table recomposés de façon dynamique. Ce, est également facile. Là où je vais avoir des problèmes est le « ce » référence à l'intérieur de la fonction qui gère l'événement. Au lieu de « ce » fait référence à l'objet, il est référence à l'élément qui a déclenché l'événement.

Code Voir. La zone problématique est en « ticketTable.prototype.handleCellClick = function () »

function ticketTable(ticks)
{
    // tickets is an array
    this.tickets = ticks;
} 

ticketTable.prototype.render = function(element)
    {
        var tbl = document.createElement(table);
        for ( var i = 0; i < this.tickets.length; i++ )
        {
            // create row and cells
            var row = document.createElement(tr);
            var cell1 = document.createElement(td);
            var cell2 = document.createElement(td);

            // add text to the cells
            cell1.appendChild(document.createTextNode(i));
            cell2.appendChild(document.createTextNode(this.tickets[i]));

            // handle clicks to the first cell.
            // FYI, this only works in FF, need a little more code for IE
            cell1.addEventListener(click, this.handleCellClick, false);

            // add cells to row
            row.appendChild(cell1);
            row.appendChild(cell2);


            // add row to table
            tbl.appendChild(row);            
        }

        // Add table to the page
        element.appendChild(tbl);
    }

    ticketTable.prototype.handleCellClick = function()
    {
        // PROBLEM!!!  in the context of this function, 
        // when used to handle an event, 
        // this is the element that triggered the event.

        // this works fine
        alert(this.innerHTML);

        // this does not.  I can't seem to figure out the syntax to access the array in the object.
        alert(this.tickets.length);
    }
Créé 27/08/2009 à 03:44
source utilisateur
Dans d'autres langues...                            


8 réponses

voix
46

Vous pouvez utiliser bind qui vous permet de spécifier la valeur qui doit être utilisé comme cela pour tous les appels à une fonction donnée.

   var Something = function(element) {
      this.name = 'Something Good';
      this.onclick1 = function(event) {
        console.log(this.name); // undefined, as this is the element
      };
      this.onclick2 = function(event) {
        console.log(this.name); // 'Something Good', as this is the binded Something object
      };
      element.addEventListener('click', this.onclick1, false);
      element.addEventListener('click', this.onclick2.bind(this), false); // Trick
    }

Un problème dans l'exemple ci - dessus est que vous ne pouvez pas supprimer l'auditeur se lier. Une autre solution utilise une fonction spéciale appelée handleEvent pour attraper tous les événements:

var Something = function(element) {
  this.name = 'Something Good';
  this.handleEvent = function(event) {
    console.log(this.name); // 'Something Good', as this is the Something object
    switch(event.type) {
      case 'click':
        // some code here...
        break;
      case 'dblclick':
        // some code here...
        break;
    }
  };

  // Note that the listeners in this case are this, not this.handleEvent
  element.addEventListener('click', this, false);
  element.addEventListener('dblclick', this, false);

  // You can properly remove the listners
  element.removeEventListener('click', this, false);
  element.removeEventListener('dblclick', this, false);
}

Comme toujours mdn est le meilleur :). Je viens de copier collé la partie de répondre à cette question.

Créé 22/10/2013 à 01:31
source utilisateur

voix
35

Vous devez gestionnaire « bind » à votre instance.

var _this = this;
function onClickBound(e) {
  _this.handleCellClick.call(cell1, e || window.event);
}
if (cell1.addEventListener) {
  cell1.addEventListener("click", onClickBound, false);
}
else if (cell1.attachEvent) {
  cell1.attachEvent("onclick", onClickBound);
}

Notez que gestionnaire d'événements ici normalise eventobjet (passé comme premier argument) et invoque handleCellClickdans un contexte (c. -à- référence à un élément qui a été attaché écouteur d'événement).

A noter également que la normalisation de contexte ici (ie réglage approprié thisdans le gestionnaire d'événements) crée une référence circulaire entre la fonction utilisée comme gestionnaire d'événements ( onClickBound) et un objet d'élément ( cell1). Dans certaines versions de IE (6 et 7) , cela peut, et probablement, entraîner une fuite de mémoire. Cette fuite en substance est navigateur ne pas libérer de la mémoire lors de l' actualisation de la page en raison de référence circulaire existant entre l' objet natif et l' hôte.

Pour contourner, vous devrez soit a) baisser la thisnormalisation; b) utiliser la stratégie de normalisation de remplacement (et plus complexe); c) « nettoyer » les écouteurs d'événements existants sur la page de déchargement, soit à l'aide removeEventListener, detachEventet les éléments nullING (qui malheureusement rendu l'historique de navigation rapide des navigateurs inutiles).

Vous pouvez aussi trouver une bibliothèque JS qui prend soin de cela. La plupart d'entre eux (par exemple: jQuery, Prototype.js, YUI, etc.) occupent habituellement opérations de nettoyage comme décrit dans (c).

Créé 27/08/2009 à 03:53
source utilisateur

voix
9

En outre, une autre façon est d'utiliser l' interface EventListener ( à partir de DOM2 !! Vous vous demandez pourquoi personne ne l'a mentionné, étant donné que c'est la façon la plus nette et destiné à une telle situation.)

-À- dire, au lieu d'un passage d' une fonction de rappel, vous passez un objet qui implémente l' interface EventListener. En termes simples, cela signifie simplement que vous devriez avoir une propriété dans l'objet appelé « handleEvent », qui pointe à la fonction de gestionnaire d'événements. La principale différence est ici, à l' intérieur de la fonction, thisse référer à l'objet passé à addEventListener. À savoir, this.theTicketTablesera l'instance d'objet dans le belowCode. Pour comprendre ce que je veux dire, regardez le code modifié soigneusement:

ticketTable.prototype.render = function(element) {
...
var self = this;

/*
 * Notice that Instead of a function, we pass an object. 
 * It has "handleEvent" property/key. You can add other
 * objects inside the object. The whole object will become
 * "this" when the function gets called. 
 */

cell1.addEventListener('click', {
                                 handleEvent:this.handleCellClick,                  
                                 theTicketTable:this
                                 }, false);
...
};

// note the "event" parameter added.
ticketTable.prototype.handleCellClick = function(event)
{ 

    /*
     * "this" does not always refer to the event target element. 
     * It is a bad practice to use 'this' to refer to event targets 
     * inside event handlers. Always use event.target or some property
     * from 'event' object passed as parameter by the DOM engine.
     */
    alert(event.target.innerHTML);

    // "this" now points to the object we passed to addEventListener. So:

    alert(this.theTicketTable.tickets.length);
}
Créé 27/03/2013 à 06:24
source utilisateur

voix
5

Je sais que c'est un ancien poste, mais vous pouvez également attribuer simplement le contexte à une variable self, jeter votre fonction dans une fonction anonyme qui appelle votre fonction avec .call(self)et passe dans le contexte.

ticketTable.prototype.render = function(element) {
...
    var self = this;
    cell1.addEventListener('click', function(evt) { self.handleCellClick.call(self, evt) }, false);
...
};

Cela fonctionne mieux que la « réponse acceptée » parce que le contexte n'a pas besoin d'être attribué une variable pour la classe entière ou globale, plutôt il est soigneusement caché dans le même méthode qui écoute l'événement.

Créé 15/02/2013 à 05:27
source utilisateur

voix
1

Fortement influencé par kamathln et la réponse de gagarine Je pensais que je pourrais aborder ce sujet.

Je pensais que vous pourriez probablement gagner un peu plus de liberté si vous mettez handeCellClick dans une liste de rappel et utilisez un objet en utilisant l'interface EventListener sur l'événement pour déclencher les méthodes de la liste de rappel avec le bon cela.

function ticketTable(ticks)
    {
        // tickets is an array
        this.tickets = ticks;
        // the callback array of methods to be run when
        // event is triggered
        this._callbacks = {handleCellClick:[this._handleCellClick]};
        // assigned eventListenerInterface to one of this
        // objects properties
        this.handleCellClick = new eventListenerInterface(this,'handleCellClick');
    } 

//set when eventListenerInterface is instantiated
function eventListenerInterface(parent, callback_type) 
    {
        this.parent = parent;
        this.callback_type = callback_type;
    }

//run when event is triggered
eventListenerInterface.prototype.handleEvent(evt)
    {
        for ( var i = 0; i < this.parent._callbacks[this.callback_type].length; i++ ) {
            //run the callback method here, with this.parent as
            //this and evt as the first argument to the method
            this.parent._callbacks[this.callback_type][i].call(this.parent, evt);
        }
    }

ticketTable.prototype.render = function(element)
    {
       /* your code*/ 
        {
            /* your code*/

            //the way the event is attached looks the same
            cell1.addEventListener("click", this.handleCellClick, false);

            /* your code*/     
        }
        /* your code*/  
    }

//handleCellClick renamed to _handleCellClick
//and added evt attribute
ticketTable.prototype._handleCellClick = function(evt)
    {
        // this shouldn't work
        alert(this.innerHTML);
        // this however might work
        alert(evt.target.innerHTML);

        // this should work
        alert(this.tickets.length);
    }
Créé 24/04/2015 à 23:26
source utilisateur

voix
0

Cette syntaxe de flèche fonctionne pour moi:

document.addEventListener('click', (event) => {
  // do stuff with event
  // do stuff with this 
});

ce sera le parent contexte et non le document de contexte

Créé 13/06/2019 à 20:14
source utilisateur

voix
0

Avec ES6, vous pouvez utiliser une fonction de flèche , comme qui utilisera une portée lexicale [0] qui vous permet d'éviter de devoir utiliser bindou self = this:

var something = function(element) {
  this.name = 'Something Good';
  this.onclick1 = function(event) {
    console.log(this.name); // 'Something Good'
  };
  element.addEventListener('click', () => this.onclick1());
}

[0] https://medium.freecodecamp.org/learn-es6-the-dope-way-part-ii-arrow-functions-and-the-this-keyword-381ac7a32881

Créé 16/04/2019 à 18:48
source utilisateur

voix
0

Qu'en est-il de

...
    cell1.addEventListener("click", this.handleCellClick.bind(this));
...

ticketTable.prototype.handleCellClick = function(e)
    {
        alert(e.currentTarget.innerHTML);
        alert(this.tickets.length);
    }

e.currentTarget pointe vers la cible qui est liée à la « cliquez sur événement » (à l'élément qui a déclenché l'événement) , tandis que

bind (ce) conserve la valeur de outerscope de l' thisintérieur de la fonction d'événement de clic.

Si vous voulez obtenir une cible exacte cliqué, utilisez e.target à la place.

Créé 18/09/2017 à 02:29
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more