Der Wert von „dies“ innerhalb des Handler addEventListener

stimmen
41

Ich habe ein Javascript-Objekt über Prototyping erstellt. Ich versuche, eine Tabelle dynamisch zu machen. Während der Rendering-Teil ist einfach und funktioniert gut, ich brauche auch bestimmte Client-Side-Events für die dynamisch gerendert Tabelle zu handhaben. Das ist auch einfach. Wo ich Probleme habe, ist mit der „dieser“ Referenz innerhalb der Funktion, die das Ereignis behandelt. Statt „dieses“ das Objekt verweist, es verweist das Element, das das Ereignis ausgelöst hat.

Siehe Code. Der Problembereich ist in 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);
    }
Veröffentlicht am 27/08/2009 um 03:44
quelle vom benutzer
In anderen Sprachen...                            


8 antworten

stimmen
46

Sie können verwenden binden , die Sie den Wert festlegen können , die als verwendet werden soll dies für alle Anrufe auf eine bestimmte Funktion.

   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
    }

Ein Problem bei dem obigen Beispiel ist , dass Sie den Hörer mit Binden nicht entfernen können. Eine andere Lösung wird mit einer speziellen Funktion namens handle alle Ereignisse zu fangen:

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

Wie immer mdn ist die beste :). Ich kopiere einfach den Teil geklebt , als diese Frage zu beantworten.

Beantwortet am 22/10/2013 um 01:31
quelle vom benutzer

stimmen
35

Sie müssen „binden“ Handler Ihre Instanz.

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

Beachten Sie, dass Event - Handler hier normalisiert eventObjekt (als erstes Argument übergeben) und ruft handleCellClickin einem richtigen Kontext (dh mit Bezug auf ein Element , das Ereignis - Listener befestigt war).

Beachten Sie auch , dass der Kontext Normalisierung hier (dh Einstellung ordnungsgemäße thisin Ereignishandler) erzeugt eine kreisförmige Referenz zwischen Funktion als Ereignisbehandlungsroutine verwendet ( onClickBound) und einem Element - Objekt ( cell1). In einigen Versionen von IE (6 und 7) dies kann und wird wahrscheinlich dazu führen , dass ein Speicherleck. Dieses Leck ist im Wesentlichen Browser auf Seitenaktualisierung aufgrund zirkulären Verweis zwischen nativen und Host - Objekt vorhandenen freizugeben Speicher versagt.

Umgehen es, müssen Sie entweder a) fallen thisNormalisierung; b) einsetzen alternative (und kompliziertere) Normalisierungsstrategie; c) „aufzuräumen“ bestehende Ereignis - Listener auf Seite entladen, dh durch Verwendung removeEventListener, detachEventund Elemente nulling (die leider machen würden Browser schnell Geschichte Navigation nutzlos).

Sie könnten auch eine JS-Bibliothek finden, das für das dauert. Die meisten von ihnen (zB: jQuery, Prototype.js, YUI, etc.) in der Regel Cleanups behandeln, wie in (c) beschrieben.

Beantwortet am 27/08/2009 um 03:53
quelle vom benutzer

stimmen
9

Auch eine weitere Möglichkeit ist es, das verwenden Eventlistener - Schnittstelle (von DOM2 !! fragen sich, warum niemand erwähnt, wenn man bedenkt es ist der sauberste Weg , und bedeutete für eine solche Situation.)

Das heißt, anstelle einer eine Rückruffunktion geben, übergeben Sie ein Objekt , das Eventlistener - Schnittstelle implementiert. Einfach gesagt, es bedeutet nur , Sie eine Eigenschaft in dem Objekt haben sollte „handle“ genannt, die auf die Event - Handler - Funktion verweist. Der wesentliche Unterschied ist hier, innerhalb der Funktion, thiswird auf das auf dem übergebenen Objekt beziehen addEventListener. Das heißt, this.theTicketTablewird die Objektinstanz in der belowCode sein. Um zu verstehen , was ich meine, sehen Sie die geänderten Code sorgfältig:

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);
}
Beantwortet am 27/03/2013 um 06:24
quelle vom benutzer

stimmen
5

Ich weiß , das ist ein älterer Beitrag, aber Sie können auch einfach den Kontext einer Variablen zuweisen self, werfen Sie Ihre Funktion in einer anonymen Funktion , die mit Ihrer Funktion aufruft .call(self)und übergibt im Rahmen.

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

Das funktioniert besser als die „akzeptierte Antwort“, weil der Kontext eine Variable für die gesamte Klasse oder global zugewiesen wird nicht benötigt, sondern es ist ordentlich versteckt innerhalb der gleichen Methode, die für das Ereignis überwacht.

Beantwortet am 15/02/2013 um 05:27
quelle vom benutzer

stimmen
1

Stark beeinflusst von kamathln und Antwort des Gagarine ich dachte ich, das angehen könnte.

Ich dachte, Sie wahrscheinlich ein bisschen mehr Freiheit gewinnen könnten, wenn Sie handeCellClick in einer Rückrufliste setzen und ein Objekt verwenden, um die Eventlistener-Schnittstelle auf der Veranstaltung mit dieser die Rückrufliste Methoden mit der richtigen auszulösen.

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);
    }
Beantwortet am 24/04/2015 um 23:26
quelle vom benutzer

stimmen
0

Dieser Pfeil Syntax funktioniert für mich:

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

dies wird die seine Eltern Kontext und nicht das Dokument Kontext

Beantwortet am 13/06/2019 um 20:14
quelle vom benutzer

stimmen
0

Mit ES6, können Sie einen Pfeil Funktion wie die lexikalischen Scoping verwenden [0], ermöglicht es Ihnen , zu vermeiden , zu verwenden bindoder 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

Beantwortet am 16/04/2019 um 18:48
quelle vom benutzer

stimmen
0

Wie wäre es mit

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

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

e.currentTarget Punkte auf das Ziel , die auf das „Klick - Ereignisse“ gebunden ist (auf das Element , das das Ereignis ausgelöst hat ) , während

bind (this) bewahrt den outerscope Wert thisinnerhalb der Click - Ereignis - Funktion.

Wenn Sie ein genaues Ziel geklickt erhalten mögen, verwenden e.target statt.

Beantwortet am 18/09/2017 um 02:29
quelle vom benutzer

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