“这个”的处理程序中使用addEventListener值“这个”的处理程序中使用addEventL

2019-06-17 10:38发布

我创建通过原型JavaScript对象。 我试图动态呈现的表。 虽然渲染部分是简单和正常工作,我还需要处理的动态呈现的表中的某些客户端的事件。 这也很容易。 当我遇到问题与处理该事件的函数里面的“this”引用。 而不是“本”参考对象,它引用引发事件的元素。

见代码。 有问题的区域是“ticketTable.prototype.handleCellClick =函数()”

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

Answer 1:

你需要“绑定”处理程序到您的实例。

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对象(作为第一个参数传递),并调用handleCellClick在适当的上下文(即指的是附着事件监听器的元件)。

此处还要注意,上下文正常化(即适当设定this在事件处理程序)将创建用作事件处理程序(功能之间循环引用onClickBound )和元素对象( cell1 )。 在IE(6和7)这可以,而且很可能会的一些版本中,导致内存泄漏。 此泄漏在本质上是浏览器未能将发布页面刷新存储器由于天然和宿主对象之间存在的循环引用。

为了克服它,你需要到任何一个)降this正常化; b)中采用的替代(和更复杂的)归一化策略; C)“清理”页上卸载现有的事件监听器,通过使用IE removeEventListenerdetachEvent和元素null荷兰国际集团(不幸的是会使浏览器的快速历史浏览没用)。

你也可以找到一个JS库,注意到了这一问题。 他们中的大多数:如在(c)中所述(例如jQuery的,的Prototype.js,锐等)通常处理清理。



Answer 2:

您可以使用绑定 ,它可以让您指定应作为作给定函数的所有调用的值。

   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
    }

在上面的例子中的一个问题是,你不能用绑定删除侦听。 另一种解决方案是使用所谓的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是最好的:)。 我只是复制粘贴比回答这个问题的一部分。



Answer 3:

此外,另一种方式是使用事件监听接口 (从DOM2 !!想知道为什么没有人提到它,认为它是最巧妙的方法,并意味着只是这样的情况。)

即,而不是通过一个回调函数,你通过它实现的EventListener接口的对象。 简单地说,它只是意味着你必须在一个名为“为handleEvent”的对象,它指向事件处理函数的性质。 主要的区别是在这里,在函数内部, this将引用传递给该对象addEventListener 。 也就是说, this.theTicketTable将在belowCode对象实例。 要明白我的意思,看仔细修改后的代码:

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


Answer 4:

我知道这是一个老帖子,但你也可以简单地分配上下文给一个变量self ,把你的功能在与调用你的函数匿名函数.call(self)并传递上下文。

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

这个作品比“接受的答案”更好,因为背景并不需要分配一个变量整个类或全球性的,而它巧妙地隐藏起来,监听事件的同样的方法中。



Answer 5:

通过kamathln和gagarine的答案的严重影响,我想我可能会解决这个。

我想你也许可以得到多一点自由,如果你把handeCellClick在回调列表,并使用该事件的事件监听接口触发具有正确此回调列表方法使用的对象。

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


Answer 6:

关于什么

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

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

e.currentTarget指向这势必“点击事件”(对引发事件的元素)的目标,而

绑定(本)保留的outerscope值this click事件函数内。

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



Answer 7:

与ES6,可以使用箭头的功能,将使用词法作用域[0],它允许你避免使用bindself = 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



文章来源: The value of “this” within the handler using addEventListener