문제

프로토 타이핑을 통해 JavaScript 객체를 만들었습니다. 테이블을 동적으로 렌더링하려고합니다. 렌더링 부분은 간단하고 잘 작동하지만 동적으로 렌더링 된 테이블의 특정 클라이언트 측면 이벤트도 처리해야합니다. 그것은 또한 쉽습니다. 내가 문제가있는 곳은 이벤트를 처리하는 함수 내부의 "이"참조와 관련이 있습니다. "이"는 객체를 참조하는 대신 이벤트를 제기 한 요소를 참조합니다.

코드를 참조하십시오. 문제가있는 영역이 있습니다 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 개체 (첫 번째 인수로 전달)를 호출합니다. handleCellClick 적절한 맥락에서 (즉, 이벤트 리스너가 첨부 된 요소를 참조).

또한 여기에서 컨텍스트 정규화 (예 : 적절한 설정 this 이벤트 핸들러에서) 이벤트 핸들러로 사용되는 기능 사이의 원형 기준을 만듭니다 (onClickBound) 및 요소 객체 (cell1). 일부 버전의 IE (6 및 7)에서는 메모리 누출을 초래할 수 있습니다. 본질적 으로이 누출은 기본 객체와 호스트 객체 사이에 기존의 원형 참조로 인해 페이지 새로 고침에 메모리를 해제하지 못하는 브라우저입니다.

우회하려면 a) 방울이 필요할 것입니다. this 표준화; b) 대안 (그리고 더 복잡한) 정규화 전략을 사용한다. c) 페이지 언로드에서 "기존 이벤트 청취자 정리", 즉 사용하여 removeEventListener, detachEvent 그리고 요소 nulling (불행히도 브라우저의 빠른 기록 탐색을 쓸모 없게 만들 것입니다).

이를 처리하는 JS 라이브러리를 찾을 수도 있습니다. 그들 중 대부분 (예 : jQuery, prototype.js, yui 등)은 일반적으로 (c)에 설명 된대로 정리를 처리합니다.

다른 팁

당신이 사용할 수있는 묶다 사용해야 할 값을 지정할 수 있습니다. 이것 주어진 함수에 대한 모든 호출.

   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
    }

위의 예에서 문제는 바인드로 리스너를 제거 할 수 없다는 것입니다. 다른 솔루션은 호출되는 특수 함수를 사용하는 것입니다 핸들 이벤트 모든 이벤트를 포착하려면 :

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에서 !! 그것이 왜 그것을 언급하지 않았는지 궁금해하고, 그것이 가장 깔끔한 방법이며 그러한 상황을위한 것입니다.)

즉, 콜백 함수를 전달하는 대신 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);
}

나는 이것이 오래된 게시물이라는 것을 알고 있지만 컨텍스트를 변수에 할당 할 수도 있습니다. self, 기능을 호출하는 익명 기능으로 기능을 던지십시오. .call(self) 그리고 맥락에서 통과합니다.

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

컨텍스트에 전체 클래스 또는 글로벌에 변수를 할당 할 필요가 없기 때문에 "수락 된 답변"보다 더 잘 작동합니다. 오히려 이벤트에 대한 리 듣는 동일한 방법 내에 깔끔하게 자리 잡고 있습니다.

Kamathln과 Gagarine의 답변에 크게 영향을받은 나는 이것을 다룰 수 있다고 생각했습니다.

HandeCellClick을 콜백 목록에 넣고 이벤트의 EventListener 인터페이스를 사용하여 콜백 목록 메소드를 올바른 상태로 트리거하면 객체를 사용하면 좀 더 자유를 얻을 수 있다고 생각했습니다.

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

이 화살표 구문은 나에게 작동합니다.

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

이것 할 것입니다 부모의 맥락이 아니라 문서 문맥

는 어때

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

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

E.CurrentTarget 대상을 가리 킵니다 "클릭 이벤트"에 묶여 있습니다. (이벤트를 일으킨 요소에)

바인딩 (this) 겉옷 값을 보존합니다 this 클릭 이벤트 기능 내부.

정확한 목표를 클릭하려면 사용하십시오. E.TARGET 대신에.

ES6을 사용하면 Arrow 함수를 사용할 수있어 어휘 스코핑 [0]을 사용하여 사용하지 않아도됩니다. bind 또는 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 this-keyword-381ac7a32881

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top