O valor de "presente" dentro do manipulador usando addEventListener

votos
41

Eu criei um objeto javascript via prototipagem. Eu estou tentando processar uma tabela dinâmica. Enquanto a parte de processamento é simples e funciona bem, eu também preciso para lidar com certos eventos do lado do cliente para a tabela apresentados dinamicamente. Isso, também é fácil. Onde eu estou tendo problemas é com o presente de referência dentro da função que manipula o evento. Em vez de isto faz referência ao objeto, ele está referenciando o elemento que disparou o evento.

Ver código. A área problemática está em 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);
    }
Publicado 27/08/2009 em 03:44
fonte usuário
Em outras línguas...                            


6 respostas

votos
46

Você pode usar o ligamento que permite especificar o valor que deve ser usado como este para todas as chamadas para uma determinada função.

   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
    }

Um problema no exemplo acima é que você não pode remover o ouvinte com bind. Outra solução é usar uma função especial chamada handleEvent para pegar todos os eventos:

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

Como sempre MDN é a melhor :). Eu só cópia colado a parte do que responder a esta pergunta.

Respondeu 22/10/2013 em 01:31
fonte usuário

votos
35

Você precisa "ligar" manipulador para sua instância.

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

Note-se que manipulador de eventos aqui normaliza eventobjeto (passado como um primeiro argumento) e invoca handleCellClickem um contexto adequado (ou seja, referindo-se a um elemento que foi anexado ouvinte de eventos para).

Além disso, note que a normalização contexto aqui (isto é, a configuração apropriada thisno processador de eventos) cria uma referência circular entre a função utilizado como processador de eventos ( onClickBound) e um objecto elemento ( cell1). Em algumas versões do IE (6 e 7), este pode, e provavelmente, resultar em um vazamento de memória. Esse vazamento, em essência, é o navegador não para liberar memória na página de atualização devido a referência circular existente entre o objeto nativo e host.

Para contornar isso, você precisa se quer a) cair thisnormalização; b) empregam alternativa (e mais complexo) estratégia de normalização; c) "limpar" os ouvintes de eventos existentes na página de descarga, ou seja, usando removeEventListener, detachEvente elementos nulling (que infelizmente tornaria rápida navegação história navegadores inútil).

Você também pode encontrar uma biblioteca JS que cuida disso. A maioria deles (por exemplo: jQuery, Prototype.js, YUI, etc.) normalmente lidar com limpezas como descrito em (c).

Respondeu 27/08/2009 em 03:53
fonte usuário

votos
9

Além disso, mais uma maneira é usar a interface EventListener (de DOM2 !! Querendo saber por que ninguém mencionou isso, considerando que é a forma mais pura e significou para apenas uma tal situação.)

Ou seja, em vez de uma passagem de uma função de retorno, você passar um objeto que implementa EventListener Interface. Simplificando, isso significa apenas que você deve ter uma propriedade no objeto chamado "handleEvent", que aponta para a função de manipulador de eventos. A principal diferença aqui é, no interior da função, thisvai referir-se ao objecto transmitido para o addEventListener. Ou seja, this.theTicketTableserá a instância do objeto no belowCode. Para entender o que quero dizer, olhe para o código modificado com cuidado:

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);
}
Respondeu 27/03/2013 em 06:24
fonte usuário

votos
5

Sei que este é um cargo mais velho, mas você também pode simplesmente atribuir o contexto para uma variável self, jogue sua função em uma função anônima que chama a sua função com .call(self)e passa no contexto.

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

Isso funciona melhor do que a "resposta aceita" porque o contexto não precisa ser atribuído uma variável para toda a classe ou global, mas sim é perfeitamente escondido dentro do mesmo método que ouve o evento.

Respondeu 15/02/2013 em 05:27
fonte usuário

votos
1

Fortemente influenciado por kamathln ea resposta de Gagarine eu pensei que eu poderia lidar com isso.

Eu estava pensando, você provavelmente poderia ganhar um pouco mais de liberdade se você colocar handeCellClick em uma lista de retorno de chamada e usar um objeto usando a interface EventListener sobre o evento para acionar os métodos de lista de chamada de retorno com a corrigir isso.

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);
    }
Respondeu 24/04/2015 em 23:26
fonte usuário

votos
0

Sobre o quê

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

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

e.currentTarget aponta para o alvo que está ligado ao "clique evento" (para o elemento que disparou o evento), enquanto

bind (este) preserva o valor outerscope de thisdentro da função de clique evento.

Se você deseja obter um alvo exato clicado, use e.target vez.

Respondeu 18/09/2017 em 02:29
fonte usuário

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