1

I have commented the relevant code. I found through trial and error that I have to use bind in both the event handler and the named callback in order for things to work correctly.

I did not find this intuitive as I thought that the named callback would call the function with a value of this pointing to the containing object literal.

I understand that the event handler needs a bind b.c. normally the callback would point to the element which triggered the event.

But the second bind, ( which I verified is required ), I don't understand the mechanism/concept.

There is a // * denoting the area of focus.

NS.parsel({
    Name: 'MSimMenu',
    E: {
        hold_name:         '#hold_name',
        wrap_bottom:       '#wrap_bottom'
    },
    A: {
        time_out_id:        null,
        TIME_DELAY:         1000
    },
    init: function () {
        var self = this;
        self.E.hold_name.addEventListener(   "mouseover",   self.mouseOverTop.bind(self),    false);
        self.E.wrap_bottom.addEventListener( "mouseover",   self.mouseOverBottom.bind(self), false);
        self.E.wrap_bottom.addEventListener( "mouseout",    self.mouseOut.bind(self),        false);
        self.E.hold_name.addEventListener(   "mouseout",    self.mouseOut.bind(self),        false);
    },

    // callbacks

    mouseOverTop: function () {
        NS.clearTimeout(this.A.time_out_id);
        this.showBottom();
    },
    mouseOverBottom: function () {
        NS.clearTimeout(this.A.time_out_id);
    },        
    mouseOut: function () {

        // * this regards the question
        // bind is required here for hideBottom to have correct value of this

        this.A.time_out_id = NS.setTimeout(this.hideBottom.bind(this), this.A.TIME_DELAY);
    },

    // called from callbacks

    showBottom: function () {
        this.E.wrap_bottom.style.visibility = 'visible';
    },
    hideBottom: function () {
        this.E.wrap_bottom.style.visibility = 'hidden';
    }
});

2 Answers2

5

That's because this is determined based on how the function is called. In the case of the setTimeout callback, it's not called in the context of your NS object, but as an independent function. In this case this is undefined, falling back to the global object.

Regarding the bound event handlers, you might be interested on an alternate method, which is making your object implement the EventHandler interface. A fellow stackoverflower has been suggesting that recently, see the accepted answer to How to Implement DOM Data Binding in JavaScript.

Community
  • 1
  • 1
bfavaretto
  • 71,580
  • 16
  • 111
  • 150
  • @pure_code.mom I'm not sure what he means by "immediate caller". But if you call `NS.hideBottom`, then `this` is NS; but if you do `var func = NS.hideBottom; func()`, then it will be the global object. Or, if you do `var o = { fn: NS.hideBottom }; o.fn()`, then `this` will be `o`. – bfavaretto Jun 11 '13 at 16:21
  • @pure_code.mom Hahaha, thanks! One more thing that may clarify this issue for you: the "bizarre" effect is because the function/method does not belong to any particular object, they're just attached as references; in fact, the same function object can have multiple "parent" objects, as the examples in my comment above shows. So JS cannot know which object to bind the function to, and you have to bind it manually. – bfavaretto Jun 11 '13 at 16:30
1

In JavaScript when a function calls another function, the the 'this' keyword changes context. The context is the immediate caller of the function.

James Culshaw
  • 1,047
  • 8
  • 19