SitePoint Sponsor

User Tag List

Results 1 to 13 of 13
  1. #1
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,122
    Mentioned
    29 Post(s)
    Tagged
    2 Thread(s)

    Losing 'this' focus in event listener

    Hello,

    I'm working on an Ajax class - though my question isn't really about Ajax at all..
    On line 47 I setup a listener onreadystatechange.
    Code:
    // Setup listener on readystatechange
    this.xhr.onreadystatechange = stateChangeHandler;
    The problem is that when Ajax.stateChangeHandler() is called
    I have lost a reference to the Ajax object using 'this'
    So I am just using Ajax.variable to access it's properties / methods - This works but it feels kind of hacky.

    Is there a way to keep the object focus within an event listener?


    Thanks,

  2. #2
    SitePoint Evangelist
    Join Date
    Jul 2007
    Posts
    345
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Maybe pass a reference to the Ajax object as an argument of the handler.

    Code:
    var me = this;
    this.xhr.onreadystatechange = function() {
      stateChangeHandler(me);
    }

  3. #3
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,122
    Mentioned
    29 Post(s)
    Tagged
    2 Thread(s)
    Thanks,

    hmm that would work but I'm just changing my reference from Ajax to 'me'. Both methods work - i'm just wondering why it's happening, and if there's any downfalls of either of these methods to get back a reference to the object.

    Is the reason it loses focus of 'this' because it is an Event Handler and not an Event Listener?

    Working through 'Simply Javascript' and enjoying the journey so far..

  4. #4
    SitePoint Evangelist
    Join Date
    Jul 2007
    Posts
    345
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You have set the event handler on this.xhr which means this.xhr should become the value of 'this' when the handler is called.

    Maybe just using this.readystate and this.response will work?

  5. #5
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,122
    Mentioned
    29 Post(s)
    Tagged
    2 Thread(s)
    Hmmm, this is bizzare,

    I added
    Code:
    var stateChangeHandler = function(type) {
      alert(this);
    It printed 'Object Window'

    I'm thinking that the Window object is the one that is calling the event handler - so 'this' is referring to the calling object within that function.

    Here's the example if you would like to mess around with it..
    http://www.yellowshoe.com.au/example...lass_test.html

  6. #6
    SitePoint Evangelist
    Join Date
    Jul 2007
    Posts
    345
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    How about not using 'this.xhr = ...' but 'var xhr = ...' instead?
    That way you'll have an xhr variable in the scope chain within Ajax. Wherever you have used this.xhr, just use xhr on its own. No problems with 'this'.

    I guess 'this' is the window because the browser itself is calling the onreadystate handler when it receives an asynchronous response from the server.

  7. #7
    SitePoint Guru
    Join Date
    Mar 2004
    Location
    Earth
    Posts
    406
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    "this" is a reference to window inside event listeners; a lot of people think it would make more sense if it was a reference to the bound object (which is what happens with DOM0 event handlers), but there's nothing in the DOM spec that says it should be. There are reams of discussion about this all over the internet, so I won't add to it - it's as it is

    but I'm just changing my reference from Ajax to 'me'. Both methods work - i'm just wondering why it's happening, and if there's any downfalls of either of these methods to get back a reference to the object.
    That works because Javascript implements scope iteration. If you refer to a variable that doesn't exist in a particular scope, the interpretor will climb the scope chain looking for it. In fact this trick is pretty much a bedrock of OO scriping where event listeners are involved, to allow you to save a reference to an object that remains available to inner scopes.

    Here's another example:
    Code JavaScript:
    function CustomObject()
    {
    	alert(this);	//a reference to this instance of CustomObject
     
    	var self = this;
     
    	someElement.onmouseover = function()
    	{
    		alert(this);	//a reference to someElement
    		alert(self);	//a reference to this instance of CustomObject
    	}
    }
     
    new CustomObject();

    In your situation, you can just use the "Ajax" variable, providing that you only have one instance of the Ajax object (ie, your script is object-based, rather than object oriented .. if you know what I mean by that difference?)

  8. #8
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,122
    Mentioned
    29 Post(s)
    Tagged
    2 Thread(s)
    Wow, thanks Brothercake - for the excellent explanation.

    That makes perfect sense now - though, could you explain the difference between 'object-based' and 'object oriented' javascript - or was that a joke?

    Your fame proceeds you - I just read a mention about you in 'Bulletproof Ajax'

  9. #9
    SitePoint Guru
    Join Date
    Mar 2004
    Location
    Earth
    Posts
    406
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    No it wasn't a joke It's like this:

    Object-based scripting is where you use object-literals to store properties and methods, which helps with encapsulation (it reduces the possibility of conflict with other scripts). Object-based scripts typically look something like this:

    Code JavaScript:
    MyObject = 
    {
    	'prop1' : 'foo', 
    	'prop2' : 'bar', 
    	'method1' : function()
    	{
    		return 'hello world';
    	}
    };

    Or like this:

    Code JavaScript:
    MyObject = new Object;
     
    MyObject.prop1 = 'foo';
    MyObject.prop2 = 'bar', 
    MyObject.method1 = function()
    {
    	return 'hello world';
    };

    And are called in ways like this:

    Code JavaScript:
    alert(MyObject.prop1);		//alerts "foo"
    alert(MyObject.method1());	//alerts "hello world"

    But that's not object oriented - you may have created an object, but you can only use a single instance of it by referring to the object name directly (MyObject).

    With object orientation, your root function is like a class, and methods of that object are extended to each instance individually, usually with prototypes:

    Code JavaScript:
    function MyObject()
    {
    	this.prop1 = 'foo';
    	this.prop2 = 'bar';
    }
     
    MyObject.prototype.method1 = function()
    {
    	return 'hello world';
    }

    In that example, your MyObject can be instantiated multiple times and each instance is entirely discreet:

    Code JavaScript:
    var instance1 = new MyObject();
     
    var instance2 = new MyObject();
    instance2.prop1 = 'aargh';
     
    alert(instance1.prop1);		//alerts "foo"
    alert(instance2.prop1);		//alerts "aargh"

    Now anything you do to instance1 only affects that instance of MyObject. If you created a new method for that instance:

    Code JavaScript:
    instance1.someNewMethod = function()
    {
     
    };

    That would only be available to instance1, not to other instances, because it's not a method of MyObject, it's a method of an instance of MyObject.

    You can see example of the difference with some of JavaScript's built-ins. For example, "Array" is an object and each instance of it is discreet:

    Code JavaScript:
    var data = new Array(1, 2, 3);
    data.splice(0, 1);

    The splice operation here only affects data - the specific instance of the Array object - it doesn't affect other arrays (other instances of the Array object). If you created a method for your array:

    Code JavaScript:
    data.myCustomMethod = function()
    {
     
    };

    It would only be available to that specific array. But if you prototyped it to the Array object:

    Code JavaScript:
    Array.prototype.myCustomMethod = function()
    {
     
    };

    It would be available to all arrays.


    Sorry it's kind hard to explain .. does that help?

  10. #10
    SitePoint Evangelist
    Join Date
    Jul 2007
    Posts
    345
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    An event handler was used, onreadystatechange, attached to this.xhr. Not an event listener.
    I know IE likes to make 'this' equal to 'window' for listeners.
    Using var = new XMLHttpRequest inside the Ajax object, with the usual try, catch alternatives, may be the way to go. That way you can have more than one Ajax object at a time using new. Important if you want many requests to be fired off at once because some browsers wont let you send multiple times from a single xhr, they just overwrite previous sends.

  11. #11
    SitePoint Guru
    Join Date
    Mar 2004
    Location
    Earth
    Posts
    406
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ah well yes, but that's still consistent - an XMLHttpRequest is bound to window, so naturally, "this" inside its event handler would point to window.

  12. #12
    SitePoint Wizard gRoberts's Avatar
    Join Date
    Oct 2004
    Location
    Birtley, UK
    Posts
    2,439
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I noticed the Call method a couple of days ago, and that seems to resolve this problem. It seems that if you want an event to call a method and keep the this correctly, then you can use method.call(this);

    http://www.webreference.com/js/column26/call.html


  13. #13
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,122
    Mentioned
    29 Post(s)
    Tagged
    2 Thread(s)
    Thanks Brothercake

    I'll be using the object-based approach when using this object.
    You only need one XMLHttpRequest object after all, but many different scripts can use the object for separate calls - so I won't have the problems of having multiples.

    Thanks again,

    gRoberts, i'll note that down for when i'm using an object-oriented approach.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •