0

Possible Duplicate:
Javascript prototype ‘this’ issue

I have an event listener which of course calls a method on an event. This method tries unsuccessfully to hold a reference to the holding objects this so that it can access other properties of the object.

There is a single comment denoting where the behavior is not understood. this_hold.Name is not accessible there as I thought it would be.

/*MUserExist
**
**
**
*/
$A.module({
    Name: 'MUserExist',
    S: {
        ClientStorage: SClientStorage,
        ComMessage: SComMessage,
        ComText: SComText,
        DynSma: SDynSma,
        DynTwe: SDynTwe,
        DynArc: SDynArc,
        AniMorphLabel: SAniMorphLabel,
        AniFlipPage: SAniFlipPage
    },
    E: {
        but: $A('#ue_but')[0],
        text: $A('#ue_go')[0],
        form: $A('#ue_fo')[0],
        check: $A('#ue_check')[0]
    },
    J: {
        box: $('#ue_box')
    },
    init: function () {
        var pipe = {},
            this_hold = this;
        this.J.box.draggable();
        this.E.but.addEventListener("click", function () {
            pipe = $A.definePipe(this_hold.Name);
            $A.machine(pipe);
        }, false);
        this.E.text.addEventListener("keypress", this.enter, false);
        this.S.AniMorphLabel.run(["ue_email",
                                  "ue_email_lab",
                                  "ue_go",
                                  "ue_pass_lab"
                                 ]);
    },
    enter: function (event) {
        var pipe = {},
            this_hold = this;
        if (event.keyCode === 13) {
            pipe = $A.definePipe(this_hold.Name); // fails here what does 'this' point to?
            $A.machine(pipe);
            event.preventDefault();
        }
    },
    pre: function (pipe) {
        var form_elements = this.E.form.elements,
            text_object = new this.S.ComText(form_elements);
        pipe.enter = this.enter;
        if ($A.Un.get('load') === '1') {
            if (!text_object.checkFull()) {
                pipe.type = 'empty';
                return this.S.ComMessage.message(pipe);
            }
            if (!text_object.checkPattern('email')) {
                pipe.type = 'email';
                return this.S.ComMessage.message(pipe);
            }
            if (!text_object.checkPattern('pass')) {
                pipe.type = 'pass';
                return this.S.ComMessage.message(pipe);
            }
        }
        pipe.page = text_object.getArray();
        pipe.proceed = true;
        pipe.page.remember = this.E.check.checked;
        return pipe;
    },
    post : function (pipe) {
        if (pipe.proceed === true) {
            this.S.ComMessage.resetView('ue_email');
            this.S.ComMessage.resetView('ue_go');
            this.S.ClientStorage.setAll(pipe.server.smalls);
            this.S.DynSma.run(pipe.server.smalls);
            this.S.DynArc.run(pipe.server.arcmarks);
            this.S.DynTwe.run(pipe.server.tweets);
            this.S.AniFlipPage.run('ma');
        } else {
            return this.S.ComMessage.message(pipe);
        }
    }
});
Community
  • 1
  • 1

3 Answers3

2

this likely points to the DOM node from which the event was triggered. Have you tried writing this to the console to inspect it?

console.log(this);
Evan Davis
  • 35,493
  • 6
  • 50
  • 57
1

Try to change how the event is being bound

this.E.text.addEventListener("keypress", this.enter, false);

to

var that = this;
this.E.text.addEventListener("keypress", function(event) {
  that.enter(event);
}, false);
Alexander
  • 23,432
  • 11
  • 63
  • 73
epascarello
  • 204,599
  • 20
  • 195
  • 236
1

this is the DOM object that generated the event. It is NOT your javascript object.

When you pass this.enter as the method for the event handler, the method enter does not stay bound to your object. If you want that to happen, you have to change your code to cause that to happen by doing something like this:

// save local copy of my object so I can refer to it in
// the anonymous function
var obj = this;
this.E.text.addEventListener("keypress", function(event) {obj.enter(event)}, false);

It is important to remember that this is set by the caller of a method/function. In this case the caller of the event handler is the event sub-system in the browser. It does not know what your object is and it's designed behavior is to set this to the DOM object that caused the event. So, if you want to call your obj.enter method, you can't just pass enter as the event handler. Instead, you make a separate function that gets called as the event handler and you then call obj.enter() from that using your object as the base so that this gets set correctly.

Another solution would be to use .bind() which also creates a stub function that binds the right this to a function call, but I don't use .bind() myself because it doesn't work in all older browsers.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • @pure_code - note I just made one quick correction in the code. – jfriend00 Dec 21 '12 at 21:04
  • would using apply() here be applicable at all?....and this works. –  Dec 21 '12 at 21:05
  • 1
    @pure_code - `.apply()` will also set the `this` pointer, but is not required here because you have an object with a method and `obj.method()` sets `this` for you in a more readable fashion. But, yes you could use `enter.apply(obj, arguments)` if you wanted in the same stub function. I just find that less readable than the code I suggested. – jfriend00 Dec 21 '12 at 21:08
  • I can't use an un-named function because I need to remove the eventListener for 2000 msec and then add it back...for this you need a reference to the callback function. –  Dec 22 '12 at 00:01
  • Does apply return a function reference so I can use it directly for the callback `..("keypress, enter.apply(obj, arguments), false);` –  Dec 22 '12 at 00:05
  • @pure_code - No. `.apply()` calls the function immediately. `.bind()` returns a reference. – jfriend00 Dec 22 '12 at 00:13
  • bind() seems like a more succinct solution? –  Jan 19 '13 at 18:48