使用addEventListener的处理程序中的“this”的值

我已经通过原型创build了一个javascript对象。 我试图dynamic地呈现一个表。 虽然渲染部分很简单,工作正常,我还需要处理dynamic渲染表的某些客户端事件。 那也很简单 我遇到的问题是处理事件的函数内部的“this”引用。 而不是“this”引用对象,它引用引发事件的元素。

看代码。 有问题的区域在“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); } 

您需要将绑定处理程序绑定到您的实例。

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

请注意,此处的事件处理程序将event对象(作为第一个parameter passing)进行handleCellClick ,并在正确的上下文(即引用附加事件侦听器的元素)中调用handleCellClick

还要注意,这里的上下文规范化(即在事件处理程序中设置正确)在用作事件处理函数( onClickBound )的函数和元素对象( cell1 )之间创build循环引用。 在某些版本的IE(6和7)中,这可能会导致内存泄漏。 这种泄漏实质上是由于在本机和主机对象之间存在循环引用,浏览器无法在页面刷新上释放内存。

为了规避它,你需要或者a)放弃this规范化; b)采用替代(更复杂)的正常化策略; c)通过使用removeEventListenerdetachEvent和元素null (不幸的是会导致浏览器的快速历史导航无用),“清理”页面上现有的事件监听器。

你也可以find一个JS库来处理这个问题。 (c)中描述的大多数(例如:jQuery,Prototype.js,YUI等)通常都会处理清理。

你可以使用bind来让你指定对所有给定函数的调用应该使用的值。

  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 } 

上面的例子中的一个问题是,你不能通过绑定来移除监听器。 另一个解决scheme是使用一个名为handleEvent的特殊函数来捕获任何事件:

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

像总是Mdn是最好的:)。 我只是复制粘贴的部分比回答这个问题。

另外,还有一种方法是使用EventListener接口 (从DOM2 !!想知道为什么没有人提到它,考虑到这是最简单的方法,只是为了这种情况)。

即,而不是通过callback函数,您传递实现EventListener接口的对象。 简而言之,这意味着您应该在名为“handleEvent”的对象中有一个属性,该对象指向事件处理函数。 这里的主要区别是,在函数内部, this将引用传递给addEventListener的对象。 也就是说, this.theTicketTable将成为下面的代码中的对象实例。 要理解我的意思,请仔细查看修改后的代码:

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

我知道这是一个较旧的post,但你也可以简单地将上下文分配给一个variablesself ,把你的函数放在一个匿名函数中,用.call(self)调用你的函数并传入上下文。

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

这比“接受的答案”更好,因为上下文不需要为整个类或全局分配一个variables,而是整齐地放在同一个侦听事件的方法中。

受到kamathln和gagarine的回答的重大影响,我想我可能会解决这个问题。

我认为如果将handeCellClick放在callback列表中,并使用事件的EventListener接口使用正确的触发callback列表方法,可能会获得更多的自由。

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

关于什么

 ... cell1.addEventListener("click", this.handleCellClick.bind(this)); ... ticketTable.prototype.handleCellClick = function(e) { alert(e.currnetTarget.innerHTML); alert(this.tickets.length); } 

e.currnetTarget指向与“click事件” (引发事件的元素) 绑定的目标

bind(this)在click事件函数内保存“this”的outerscope值。

如果你想获得确切的目标点击使用e.target来代替。