Ich habe ein Javascript-Objekt per Prototyping erstellt. Ich versuche, eine Tabelle dynamisch zu rendern. Während der Rendering-Teil einfach ist und gut funktioniert, muss ich auch bestimmte clientseitige Ereignisse für die dynamisch gerenderte Tabelle behandeln. Das ist auch einfach. Ich habe Probleme mit der Referenz "this" innerhalb der Funktion, die das Ereignis behandelt. Anstatt "dies" auf das Objekt zu verweisen, verweist es auf das Element, das das Ereignis ausgelöst hat.
Siehe Code. Der Problembereich liegt 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);
}
quelle
render
, kurz vor dem Anhängen des Ereignis-Listeners. Sie können so ziemlich einfach dieaddEventListener
Zeile aus dem Originalbeispiel durch dieses Snippet ersetzen .removeEventListener
/detachEvent
und brechen Sie Zirkelverweise. Hier ist eine gute Erklärung für ein Leck - jibbering.com/faq/faq_notes/closures.html#clMemSie 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 im obigen Beispiel ist, dass Sie den Listener nicht mit bind entfernen können. Eine andere Lösung ist die Verwendung einer speziellen Funktion namens handleEvent , um Ereignisse abzufangen :
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 ist mdn das Beste :). Ich habe nur den Teil kopiert und dann diese Frage beantwortet.
quelle
Eine weitere Möglichkeit ist die Verwendung des EventListener-Interfaces (von DOM2 !! Ich frage mich, warum niemand es erwähnt hat, da es der sauberste Weg ist und für eine solche Situation gedacht ist.)
Das heißt, anstatt eine Rückruffunktion zu übergeben, übergeben Sie ein Objekt, das die EventListener-Schnittstelle implementiert. Einfach ausgedrückt bedeutet dies nur, dass Sie im Objekt eine Eigenschaft namens "handleEvent" haben sollten, die auf die Ereignishandlerfunktion verweist. Der Hauptunterschied besteht darin, dass innerhalb der Funktion
this
auf das an das übergebene Objekt verwiesen wirdaddEventListener
. Das heißt,this.theTicketTable
wird die Objektinstanz im folgenden Code sein. Um zu verstehen, was ich meine, schauen Sie sich den geänderten Code genau an: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); }
quelle
removeEventListener()
?addEventListener
. Sie können hier als Referenz suchen: stackoverflow.com/a/15819593/2816199 .addEventListener
mit einem Objekt als Rückruf kompiliert . Teufel noch mal.Mit ES6, können Sie einen Pfeil Funktion wie die lexikalischen Scoping verwenden [0], ermöglicht es Ihnen , zu vermeiden , zu verwenden
bind
oderself = 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
quelle
Diese Pfeilsyntax funktioniert bei mir:
document.addEventListener('click', (event) => { // do stuff with event // do stuff with this });
dies wird die seine Eltern Kontext und nicht das Dokument Kontext
quelle
Ich weiß, dass dies ein älterer Beitrag ist, aber Sie können den Kontext auch einfach einer Variablen zuweisen
self
, Ihre Funktion in eine anonyme Funktion werfen, die Ihre Funktion mit aufruft.call(self)
und den Kontext weitergibt.ticketTable.prototype.render = function(element) { ... var self = this; cell1.addEventListener('click', function(evt) { self.handleCellClick.call(self, evt) }, false); ... };
Dies funktioniert besser als die "akzeptierte Antwort", da dem Kontext keine Variable für die gesamte Klasse oder global zugewiesen werden muss, sondern ordentlich in derselben Methode versteckt ist, die auf das Ereignis wartet.
quelle
cell1.addEventListener('click', this.handleCellClick.bind(this));
. Lassen Sie das letzte Argument,false
wenn Sie Kompatibilität mit FF <= 5 wünschen..bind()
Methode zu verwenden, musste sie aber.call()
stattdessen verwenden, da unsere App IE8 noch eine Weile unterstützen muss.self
, bis ichself
das gleiche gefunden habewindow
, jetzt benutze ichme
.removeEventListener
diesen Weg?Stark beeinflusst von Kamathln und Gagarines Antwort dachte ich, ich könnte das angehen.
Ich dachte, Sie könnten wahrscheinlich ein bisschen mehr Freiheit gewinnen, wenn Sie handeCellClick in eine Rückrufliste einfügen und ein Objekt verwenden, das die EventListener-Schnittstelle für das Ereignis verwendet, um die Rückruflistenmethoden mit dem richtigen Wert 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); }
quelle
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 zeigt auf das Ziel, das an das " Klickereignis " (an das Element, das das Ereignis ausgelöst hat) gebunden ist
bind (this) behält den Outerscope-Wert
this
innerhalb der Click-Event-Funktion bei.Wenn Sie ein genaues Ziel anklicken möchten, verwenden Sie stattdessen e.target .
quelle