SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    SitePoint Member
    Join Date
    Nov 2006
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Extending an object within an object

    I have instantiated an object (XmlDom as "oXmlDom") in my own class. I then needed to extend the object's (XmlDom) onreadystatechange method so I do this.oXmlDom.onreadystatechange = function () {...} ... However, I am unable to reference the object properly anymore. If I do alert((this instanceof XmlDom)); I get false. I can't figure out why, or how to extend this method properly. Can anyone help with ideas? The code is below:

    Code:
    function dynGridObject(shtmlObjectId) {
        this.oXmlDom = new XmlDom();        
        this.sHref = "http://www.unlistedairplane.com/milan/random.php";
        this.iLast = null;                                                  //Unique id of the last record displayed
        this.iLastOffset = 0;                                               //The pixel position of the last grid element
        this.bIsLoading = false;                                            
               
        this.oStatus = document.getElementById('status');         
        this.oLoading = document.getElementById('loading');    
        
        var oContainer = document.getElementById(shtmlObjectId);
        this.oGridFrame = document.createElement('iframe');     
        this.oGridFrame.style.height = 300;
        this.oGridFrame.style.float = 'left';
        oContainer.appendChild(this.oGridFrame);   
                
        this.doc = this.oGridFrame.contentDocument;    
        if (this.doc == undefined || this.doc == null)
            this.doc = this.oGridFrame.contentWindow.document;    
        this.doc.open();
        this.doc.write('<html><head><style>body{margin: 0px;}</style></head><body margin="0" onscroll="parent.window.doLoad();"></body></html>');                
        this.doc.close();
            
        this.oStatus.innerHTML = 'Initial Load';  
        
        this.oXmlDom.onreadystatechange = function () {
                alert((this instanceof XmlDom)); // Returns false;
                /*
                    'this' is not a reference to XmlDom
                */
                
                if (this.oXmlDom.readyState == 4) {
                    var oContent = oXmlDom.childNodes(1);
                    
                    for(var i=0; i < oContent.childNodes.length; i++) {                                                                                                                      
                        doc.body.innerHTML = doc.body.innerHTML + '<div id="' + oContent.childNodes(i).text + '">' + oContent.childNodes(i).text + '</div>';
                        iLast = oContent.childNodes(i).text
                    }               
                    
                    this.bIsLoading = false;
                    this.oStatus.innerHTML = 'Loading Complete: oXMLDom.readState = ' + oXmlDom.readyState;                  
                    oLoading.style.display = 'none';
                    iLastOffset = doc.getElementById(iLast).offsetTop + 20; // add 20 for div height
                     
                    var wHieght = (document.all) ? doc.body.clientHeight : doc.window.innerHeight;                  
                    if(iLastOffset <= wHieght)
                        oXmlDom.load(sHref + '?id=' + iLast);
                }
        }                    
                
        this.oXmlDom.load(this.sHref);        
    }

  2. #2
    Awesome Addict
    Join Date
    Mar 2004
    Location
    Toronto, Canada
    Posts
    326
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Post

    Within onreadystatechange "this" no longer refers to dynGridObject: it's refering to the readystatechange. So something like
    Code:
    this.oXmlDom.onreadystatechange = function () {
      alert(this.oXmlDom);
    }
    ...will fail because oXmlDom has nothing to do with the readystatechange function. oXmlDom is a member of the dynGridObject object and therefore must be referenced as such:
    Code:
    this.oXmlDom.onreadystatechange = function () {
      // shoukd work
      alert(dynGridObject.oXmlDom);
    }
    So within the onreadystatechange handler wherever "this" to refers to a dynGridObject member you'll have to reference it as "dynGridObject.member" instead.

    HTH... Greg

  3. #3
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Since "this" in javascript is dynamic, you have to "bind" current "this" to a function.

    Code:
    // similar to that found in Prototype
    Function.prototype.bind = function(o) {
         var __m = this;
         return function() {__m.apply(o, arguments ) }
    }
    Later in your code you can just append bind() to function definition to have it use lexically scoped "this":

    Code:
    this.oXmlDom.onreadystatechange = function () {
        ....
    }.bind(this)

  4. #4
    SitePoint Wizard
    Join Date
    Mar 2001
    Posts
    3,537
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ... oXmlDom has nothing to do with the readystatechange function
    You're saying oXmlDom has nothing to do with the onreadystatechange function? Isn't onreadystatechange a property of the oXMmlDom object? And wasn't a function assigned to that onreadystatechange property of oXmlDom? Look at the following code:
    Code:
    window.id = "I'm a Window.";  //in js, you can add properties to any object
    
    function Apple()
    {
    	this.id = "I'm an Apple.";
    }
    
    function Basket()
    {
    	this.id = "I'm a Basket.";
    	this.fruit = new Apple();
    	this.fruit.onreadystatechange = function ()  //you can name a property anything
    	{
    		alert(this.id);
    	}
    }
    	
    var b = new Basket();
    b.fruit.onreadystatechange(); //What ouput here?
    According to you, the Basket object 'b' has nothing to do with the onreadystatechange function, so the line:

    alert(this.id)

    won't display the Basket's id. What does it display?

    I can't figure out why, or how to extend this method properly.
    Run the code above. The output you see is what you are expecting to happen with your code, but as you discovered, it doesn't work in your case. Now, try adding the following line after the last line of code in the example above:
    Code:
    setTimeout(b.fruit.onreadystatechange, 3000);
    Run the code, and make sure you wait for the setTimeout to display the alert. Now, what is the output? Confounding isn't it. How can we explain the difference? Notice that in this line:

    b.fruit.onreadystatechange();

    the object 'b' is executing the function. How do we know 'b' is executing the function? Because the function execution operator "()" follows the function name. Now examine this line:

    setTimeout(b.fruit.onreadystatechange, 3000);

    the function execution operator isn't after the function name. Instead, that line hands a function reference to js, and the code tells js to execute the function referred to by that reference at a time that is 3 seconds in the future. Therefore, js actually executes the function. And from the output, you can see that js uses the window object to call the function using the function reference.

    In your oXmlDom example, you have a similar situation. In the line:

    this.oXmlDom.onreadystatechange = function () {....};

    oXmlDom isn't executing your anonymous function. Nothing is executing the function in that line because there isn't a function execution operator "()" after the function definition. That isn't as nonsensical as it might sound because in js you can both define and execute a function at the same time:
    Code:
    var result = function greeting() {return "hello"} ();
    alert(result);
    Instead, the line:

    this.oXmlDom.onreadystatechange = function () {....};

    assigns a function reference to the onreadystatechange property of an oXmlDom object. So, what object eventually executes your function? Without seeing the rest of your code, I can't say for sure, but I'd guess it's the window object--like in the example above with the setTimeout(). Try adding:

    window.greeting ="Hello";

    outside of any function definition and then for the first line of your onreadystatechange function(before any if statements) add:

    alert(this.greeting);

    and see what the output is when you send out an XMLHttpRequest.

    An easy way to correct your problem is by adding:

    var theObj = this;

    to the surrounding function definition. Then in your inner function, use theObj instead of 'this'. For example, applying those steps to the example above with the setTimeout() problem:
    Code:
    window.id = "I'm a Window.";  
    
    function Apple()
    {
    	this.id = "I'm an Apple.";
    }
    
    function Basket()
    {
    	var theObj = this;
    	
    	this.id = "I'm a Basket.";
    	this.fruit = new Apple();
    	this.fruit.onreadystatechange = function ()
    	{
    		alert(theObj.id);
    	}
    }
    	
    var b = new Basket();
    b.fruit.onreadystatechange(); 
    
    setTimeout(b.fruit.onreadystatechange, 3000);
    Compare the output now with that of the ouput earlier. Now the code works like you want because an inner function carries around a snapshot of the local variables and parameters of a surrounding function in which it is defined. 'this' is not a variable or a parameter--it refers to different things depending on the time and place the code is executed. When this code executes:

    var theObj = this;

    the assignment locks in the value of theObj to whatever 'this' happens to refer to at the moment the assignment takes place. 'this' can later change what it refers to, but that won't change the value of theObj that's preserved in the snapshot the inner function carries around with it.
    Last edited by 7stud; Nov 13, 2006 at 04:59.

  5. #5
    SitePoint Member
    Join Date
    Nov 2006
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks stud! You deserve a round of beer for this one. I will modify my code and let you know how I make out.

    Thanks again!

  6. #6
    SitePoint Member
    Join Date
    Nov 2006
    Posts
    7
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You are a genius 7stud! The "var theObj = this;" method worked like a charm. I learned something new here. Where are you from -- Can I buy you a beer!!


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
  •