Josué Boisvert
Josué Boisvert

Reputation: 43

Typescript Cannot read property 'addEventListener' of undefined

I fail to understang what is missing. The following code will return this error:

Uncaught TypeError: Cannot read property 'addEventListener' of undefined"

class Editor{
    public context: CanvasRenderingContext2D;
    constructor( public canvas: HTMLCanvasElement){
        this.context = this.canvas.getContext("2d");
        this.canvas.addEventListener("mousedown", this.handleMouseDown);
    }
    handleMouseDown(ev: MouseEvent){
        this.canvas.addEventListener("mousemove", this.handleMouseMove);
        //this.handleMouseMove(ev);
    }
    handleMouseMove(ev: MouseEvent){
        console.log(ev);
        console.log(ev.target);
    }
}

//==============================================================================
//declare variables.

let mapEditor = new Editor(<HTMLCanvasElement>document.getElementById("map-editor"));

while this one does not:

let cnv = document.getElementById("map-editor") as HTMLCanvasElement;
cnv.addEventListener("mousedown", handleMouseDown);

function handleMouseDown(ev: MouseEvent){
    ev.target.addEventListener("mousemove", handleMouseMove);
    handleMouseMove(ev);
};

function handleMouseMove(ev: MouseEvent){
    console.log(ev);
    console.log(ev.target);
};

I've tried to look around and change the order a little bit but I can't get the event to fire. Perhaps I missing an important element of the language?

Edit: I think that when the mousedown event is triggered, the context changed and thus the "this" keyword no longer refer to the class instance. Therefor this.canvas is undefined. I will try to change the way I bind methode with event.

I found an explanation here: 'this'-in-TypeScript

Upvotes: 1

Views: 2996

Answers (1)

Josu&#233; Boisvert
Josu&#233; Boisvert

Reputation: 43

The problem comes from the changing context. The class Editor will add an event listener to the canvas, in this case, "mousedown". But when the event is fired and the method is called, 'this' will refer to the canvas element and not to the containing class. Therefore, if I try to access a member, in this case, 'canvas', it is undefined. The solution, as suggested by Alex, was to lexically bind 'this' to the class instance when calling the method by using the fat arrow, as follows:

myClass.member.addEventListener("event", (e) => myClass.handleEvent(e));

Here is my solution:

class Editor{
    public context: CanvasRenderingContext2D;
    public rect: ClientRect;
    constructor( public canvas: HTMLCanvasElement){
        this.context = this.canvas.getContext("2d");
        this.rect = this.canvas.getBoundingClientRect();
        this.canvas.addEventListener("mousedown", (ev) => this.handleMouseDown(ev));
    }
    handleMouseDown(ev: MouseEvent){
        console.log(this);
        this.canvas.addEventListener("mousemove", (ev) => this.handleMouseMove);
    }
    handleMouseMove(ev: MouseEvent){
        console.log(this);
    }
    getGridCoordinate(x: number, y: number){
    //This should return the correct grid coordinate.
        return {
            x: x - this.rect.left,
            y: y - this.rect.top
        };
    }
}

Upvotes: 3

Related Questions