문제

I'm writing a small object-oriented wrapper around a canvas object, and I'd like to set some button callback to actual function members, instead of globally scoped functions (this way I can refer the context object as a member).

function CanvasManager(canvasId) {
    this.canvas = document.getElementById(canvasId);    
    this.ctx = this.canvas.getContext('2d');    

    var imageLoader = document.getElementById('imageLoader');
    imageLoader.addEventListener('change', this.handleImage, false);

    var lineNumberField  = document.getElementById('linenumber');
    lineNumberField.addEventListener('change', this.onLineNumberChange, false);
}

function handleImage = function(e) {
    var reader = new FileReader();    
    var that = this;

    reader.onload = function(event){
        var img = new Image();

        img.onload = function(){
            that.canvas.width = img.width;
            that.canvas.height = img.height;
            that.ctx.drawImage(img,0,0);
        };

        img.src = event.target.result;
    };

    reader.readAsDataURL(e.target.files[0]);     
};

This is called in an inline script in the html:

<div id="canvas-container" style="display:none;">
    <canvas id="imageCanvas"></canvas>
</div>
    ...
<script lang="javascript">new CanvasManager('imageCanvas');</script>

This doesn't work because in the handleImage callback, "this" doesn't refer to the CanvasManager instance, but the imageLoader instance, to my great confusion.

What am I doing wrong here?

도움이 되었습니까?

해결책

sorry for the comment - actually, I had misinterepreted the issue as you described it if the issue was inside the second method.

As the others implied, the event listener is going to call the function with window as the this object. Don't worry, even as a JS guru I agree it doesn't make a lot of sense.

While you can simply put everything in closures and put this into a constant var (ie, me or self) my preference is to customize the call to the function to always have a particular this reference. You can do this like so:

imageLoader.addEventListener('change', this.handleImage.bind(this), false);

bind() is a relatively new JavaScript function that gives you a new method, representing the original, but called with a particular this context. Some JavaScript libraries have equivalents that are compatible with older browsers, like Dojo's "hitch". But, since your code involves a <canvas> anyway, you should be mostly okay for compatibility.

다른 팁

try this

function CanvasManager(canvasId) {
    this.canvas = document.getElementById(canvasId);    
    this.ctx = this.canvas.getContext('2d');    

    var imageLoader = document.getElementById('imageLoader');
    imageLoader.addEventListener('change', this.handleImage, false);

    var lineNumberField  = document.getElementById('linenumber');
    lineNumberField.addEventListener('change', this.onLineNumberChange, false);
    var self = this; // keep CanvasManager instance
    this.onLineNumberChange  = function(){
    };
    this.handleImage  = function(){
       console.log(self); // use CanvasManager instance
    }
}

It looks like your function handleImage is outside your main function (or class) that you are trying to use. I would update the code as below:

function CanvasManager(canvasId) {
    var me = this;

    me.canvas = document.getElementById(canvasId);    
    me.ctx = me.canvas.getContext('2d');    

    var imageLoader = document.getElementById('imageLoader');
    imageLoader.addEventListener('change', me.handleImage, false);

    var lineNumberField  = document.getElementById('linenumber');
    lineNumberField.addEventListener('change', me.onLineNumberChange, false);

    me.handleImage = function(e) {
        var reader = new FileReader();    

        reader.onload = function(event){
            var img = new Image();

            img.onload = function(){
                me.canvas.width = img.width;
                me.canvas.height = img.height;
                me.ctx.drawImage(img, 0, 0);
            };

            img.src = event.target.result;
        };

        reader.readAsDataURL(e.target.files[0]);     
    }
}

By moving the handleImage inside the function it is now in scope of the class. Also by using me = this at the start of the method to always have access to it's class instance as the this will by default reference the objects who's event triggered it.

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