SitePoint Sponsor

User Tag List

Results 1 to 8 of 8
  1. #1
    SitePoint Member
    Join Date
    Nov 2005
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Objects and "this" substitution when calling methods

    Hi there, i'm trying, as a proof of concept, to build an object-oriented javascript gui library that allows to create on runtime an html gui from javascript scripts, with a java-like syntax (components, containers, eventListeners, etc).

    As i wanted a smooth animation when moving objects on the screen, i started implementing a CAnimation class. And here comes the problem: whenever an HTML button in my interface calls a CAnimation object's method, the "this" is defined as the button, instead of the CAnimation object, wich causes errors when calling other methods from the same object.

    Significant code:
    The interface
    PHP Code:
    <html>
        <
    head>
            <
    title>
                
    Javascript test
            
    </title>
            <
    script type="text/javascript" src="mpt/web/util/common.js"></script>
            <script type="text/javascript" src="app/bin/Main.js"></script>
            <link rel="stylesheet" type="text/css" href="style.css" />
        </head>

        <body onload='main();'>

            <div id="controles">
                <h2>Control Panel</h2>
                <input type="button" value="Start" id="btn_start" />
                <input type="button" value="Stop" id="btn_stop" /> 
                <br /><br />
                Console: <br /><br /> <div id="lbl_console"></div>
            </div>
        <body>
    </html> 
    As you have guessed the buttons call the methods (i assign the event association from the "main" function, called on the "body onload" event.)
    PHP Code:
    function main()
    {
        [...] 
    //Some init code before

        //Setting the animation object
        
    var animation = new CAnimator();
        
    animation.setTimeInterval(39);
        
    animation.setLoopFunc(doSomething);

    //Event handling for the html interface    
    document.getElementById('btn_start').addEventListener("click",animation.start,false);
         
    document.getElementById('btn_stop').addEventListener("click",animation.stop,false);

        [...] 
    //Some code after
    }

    function 
    doSomething()
    {
        
    alert("I'm doing something, you punk");

    Now, when the start method of the animation object is called, it is supposed to run with a perpetual timeout the associated loop func (as set on the main function), such as in:
    PHP Code:
    function CAnimation()
    {
        
    //Some attributes
            
    [...]
        
        
    //Encapsulation of the attributes
            
    [...]
        
        
    //Class methods
        
    this.run = function()
        {
            
    this.chronoId setTimeout(this.run,this.getTimeInterval());
            
    this.loopFunc();
        }
        
        
    this.start = function()
        {
            
    this.run();
        }
        
        
    this.stop = function()
        {
            
    clearTimeout(this.chronoId);
        }

    At last...the problem arises when i press the start button on the html interface, wich calls the start method of the animation object created in the main function, wich in turn tries to call the run method...that is, it tries but tells me that "this.run() isn't a method". I started the debugger (Venkman from within firefox) and looked at "this" to see what object it holded, and to my surprise, it told me that "this" was referencing the html input object (button) that called animation.start(), instead of the animation object itself. So when this.run is called within the animation.start() method, this references the html input object, wich of course doesn't have a run method.

    żAny idea of why is this happening, and what could i do to fix it?

    PS: Excuse my poor english, it isn't my native language as you might have guessed by now.

  2. #2
    SitePoint Wizard
    Join Date
    Nov 2004
    Location
    Portsmouth UK
    Posts
    1,489
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    may help

    Code:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
    
    <html>
    
    <head>
      <title></title>
    <script language="JavaScript" type="text/javascript">
    <!--
    
    
    function CAnimator(){
    
         //Some attributes
    //        [...]
    
        //Encapsulation of the attributes
    //        [...]
    
        //Class methods
        this.run = function(){
        alert('run')
     //        this.chronoId = setTimeout(this.run,this.getTimeInterval());
    //        this.loopFunc();
        }
    
        this.start = function()
        {
        alert(this.id+' start')
            this.run();
        }
    
        this.stop = function()
        {
            this.run();
        alert(this.id+' stop')
    //        clearTimeout(this.chronoId);
        }
    }
    
    function main()
    {
    //    [...] //Some init code before
       obj= document.getElementById('btn_start')
        //Setting the animation object
       obj.animation = new CAnimator();
       obj.animation.id='btn_start';
       document.getElementById('btn_stop').animation=obj.animation;
    
    //Event handling for the html interface
       zxcAddFind(obj);
       zxcAddFind(document.getElementById('btn_stop'));
    
    
    //  [...] //Some code after
    }
    
    function zxcFind(){
     if (this.id=='btn_start'){
      this.animation.start();
     }
     if (this.id=='btn_stop'){
      this.animation.stop();
     }
    }
    
    function zxcEventAdd(zxco,zxct,zxcf) {
     if ( zxco.addEventListener ){ zxco.addEventListener(zxct, function(e){ zxco[zxcf](e);}, false); }
     else if ( zxco.attachEvent ){ zxco.attachEvent('on'+zxct,function(e){ zxco[zxcf](e); }); }
     else {
      var zxcPrev=zxco["on" + zxct];
      if (zxcPrev){ zxco['on'+zxct]=function(e){ zxcPrev(e); zxco[zxcf](e); }; }
      else { zxco['on'+zxct]=zxco[zxcf]; }
     }
    }
    
    function zxcAddFind(zxc){
     if (zxc.addFind){ return; }
     zxc.addFind=zxcFind;
     zxcEventAdd(zxc,'click','addFind');
    }
    
    
    function doSomething()
    {
        alert("I'm doing something, you punk");
    }
    //-->
    </script>
    
    </head>
    
       <body onload='main();'>
    
            <div id="controles">
                <h2>Control Panel</h2>
                <input type="button" value="Start" id="btn_start" />
                <input type="button" value="Stop" id="btn_stop" />
                <br /><br />
                Console: <br /><br /> <div id="lbl_console"></div>
            </div>
        <body>
    </html>

  3. #3
    SitePoint Wizard
    Join Date
    Mar 2001
    Posts
    3,537
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    var animation = new CAnimator();
    Where is CAnimator()?

  4. #4
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    loic_sephiroth

    Welcome to Sitepoint!

    You may be surprised but your question is asked very often here. The answer is that "this" in javascript is dynamically scoped, i.e. it behaves more like a function than a variable. In a method, "this" always denotes method target, not the method definition context.

    I believe you'll find more info on this in "Links & Resources" topic

  5. #5
    SitePoint Addict dek's Avatar
    Join Date
    Oct 2004
    Location
    UK
    Posts
    352
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Off the top of my head, this might help also. Please forgive if not, I've had a long day.

    document.getElementById('btn_start').addEventListener("click",function() {animation.start(); },false);
    document.getElementById('btn_stop').addEventListener("click",function() { animation.stop(); },false);

    Oh - and:

    this.run = function()
    {
    oThis = this;
    this.chronoId = setTimeout(function() { oThis.run(); },this.getTimeInterval());
    this.loopFunc();
    }
    Only dead fish go with the flow

  6. #6
    SitePoint Member
    Join Date
    Nov 2005
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    For all that have answered, thanks a lot! And thanks for the welcoming too, stereofrog.

    Quote Originally Posted by 7stud
    Where is CAnimator()?
    Ops, that's right, i rewrote the code here because it was in spanish, and i made a mistake while doing it. Where i wrote var animation = CAnimator(), you should read var animation = new CAnimation() Sorry for that, and thanks for pointing it out.

    Quote Originally Posted by Dek
    Off the top of my head, this might help also. Please forgive if not, I've had a long day.

    document.getElementById('btn_start').addEventListener("click",function() {animation.start(); },false);
    document.getElementById('btn_stop').addEventListener("click",function() { animation.stop(); },false);
    I had been given this same advice from another forum (i posted on several forums the same text to maximize the chances of getting an answer), and i applied it. Only to get the run method well called the first time with "this" = animation, but when run gets called a second time by the timeout function "this" = window.

    Also
    this.run = function()
    {
    oThis = this;
    this.chronoId = setTimeout(function() { oThis.run(); },this.getTimeInterval());
    this.loopFunc();
    }
    I don't get what changes here when you first do oThis = this and then set function(){oThis.run();}.

    Quote Originally Posted by Stereofrog
    You may be surprised but your question is asked very often here. The answer is that "this" in javascript is dynamically scoped, i.e. it behaves more like a function than a variable. In a method, "this" always denotes method target, not the method definition context.

    I believe you'll find more info on this in "Links & Resources" topic
    Yes, i thougt such a beggining issue with OOP in javascript would have been asked many times before me, but when trying to look on the already posted threads and in the Tips, Links and Ressources topics i did not found something that would suit me, hence the thread i opened.

    Thanks nonetheless for the tip.

    vwphillips:
    Thanks a lot for all the code, i'm trying it right now.

  7. #7
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by loic_sephiroth

    Also
    I don't get what changes here when you first do oThis = this and then set [i
    function(){oThis.run();}[/i].
    I thought I explained that aldready. "this" is (much like) a function, if you want to use its return value, this value should be stored somewhere.

    Compare
    Code:
    func1 = function() { currentContext().doSometing() }
    
    var savedContext = currentContext()
    func2 = function() { savedContext.doSometing() }
    func1 calls currentContext() function, therefore it uses call-time context (which is active when func1 is _called_).

    func2 doesn't call currentContext(), it uses stored value and therefore definition-time context (which was active where func2 was _defined_)

    "this" behaves exactly as "currentContext()" above. The only (confusing) difference is that it doesn't use brackets when called.

  8. #8
    SitePoint Member
    Join Date
    Nov 2005
    Posts
    4
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ah, i now see much clearer that way. Thank you very much :-)

    I tried with the savedContext metaphore, and it worked!!! Thanks again!


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
  •