type ts2339 tipo propiedad property not htmlelement existe exist error does javascript javascript-events oop

javascript - ts2339 - El valor de "this" dentro del controlador mediante addEventListener



la propiedad ''value'' no existe en el tipo ''htmlelement'' angular (6)

Creé un objeto javascript mediante creación de prototipos. Estoy tratando de renderizar una tabla dinámicamente. Si bien la parte de representación es simple y funciona bien, también necesito manejar ciertos eventos del lado del cliente para la tabla renderizada dinámicamente. Eso, también es fácil. Donde estoy teniendo problemas es con la referencia "this" dentro de la función que maneja el evento. En lugar de "esto" hace referencia al objeto, hace referencia al elemento que generó el evento.

Ver código. El área problemática está en "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); }


Además, una forma más es utilizar la Interfaz EventListener (desde DOM2. Preguntarse por qué nadie lo mencionó, teniendo en cuenta que es la mejor manera y está destinado para tal situación).

Es decir, en lugar de pasar una función de devolución de llamada, pasas un objeto que implementa la interfaz EventListener. En pocas palabras, simplemente significa que debe tener una propiedad en el objeto llamado "handleEvent", que apunta a la función del controlador de eventos. La principal diferencia aquí es, dentro de la función, this se referirá al objeto pasado al addEventListener . Es decir, this.theTicketTable será la instancia del objeto en el código belowCode. Para entender lo que quiero decir, mira el código modificado cuidadosamente:

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


Debe "vincular" el controlador a su instancia.

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

Tenga en cuenta que el controlador de eventos aquí normaliza el objeto de event (pasado como un primer argumento) e invoca handleCellClick en un contexto apropiado (es decir, refiriéndose a un elemento al que se adjuntó el detector de eventos).

También tenga en cuenta que la normalización de contexto aquí (es decir, estableciendo this en el controlador de eventos) crea una referencia circular entre la función utilizada como controlador de eventos ( onClickBound ) y un objeto de elemento ( cell1 ). En algunas versiones de IE (6 y 7), esto puede generar, y probablemente lo hará, una pérdida de memoria. En esencia, esta fuga es que el navegador no puede liberar memoria en la actualización de página debido a la referencia circular existente entre el objeto nativo y el objeto host.

Para eludirlo, necesitaría a) a) abandonar this normalización; b) emplear una estrategia de normalización alternativa (y más compleja); c) "limpiar" oyentes de eventos existentes en la descarga de la página, es decir, mediante el uso de removeEventListener , detachEvent y elementos null ing (lo que desafortunadamente haría que la navegación rápida del historial de los navegadores fuera inútil).

También puede encontrar una biblioteca JS que se encargue de esto. La mayoría de ellos (p. Ej., JQuery, Prototype.js, YUI, etc.) generalmente manejan las limpiezas como se describe en (c).


Muy influenciado por la respuesta de Kamathln y Gagarine, pensé que podría abordar esto.

Pensaba que probablemente podría obtener un poco más de libertad si colocaba hanCellClick en una lista de devolución de llamada y utilizaba un objeto usando la interfaz EventListener en el evento para activar los métodos de la lista de devolución de llamada con la corrección de esto.

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


Puede usar bind que le permite especificar el valor que se debe usar como esto para todas las llamadas a una función dada.

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 }

Un problema en el ejemplo anterior es que no puede eliminar el oyente con bind. Otra solución es usar una función especial llamada handleEvent para atrapar cualquier evento:

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

Como siempre mdn es el mejor :). Copié la parte pegada antes que responder esta pregunta.


Qué pasa

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

e.currentTarget apunta al objetivo que está vinculado al "evento de clic" (al elemento que provocó el evento) mientras

bind (this) conserva el valor de outerscope de this dentro de la función click event.

Si desea hacer clic en un objetivo exacto, use e.target en su lugar.


Sé que esta es una publicación más antigua, pero también puedes simplemente asignar el contexto a un self variable, lanzar tu función en una función anónima que invoca tu función con .call(self) y pasa en el contexto.

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

Esto funciona mejor que la "respuesta aceptada" porque al contexto no se le debe asignar una variable para toda la clase o global, sino que está meticulosamente escondido dentro del mismo método que escucha el evento.