Passing values with this keyword using setTimeout

I need some more advice on using the this keyword and passing it through a setTimeout thing.

I’m new to this and made up a simple script to make things clear.

This following script changes style when mousing over a paragraph:


<html>
<head>
 <script type="text/javascript">
 
   function changeNew(){
     this.style.borderBottom = 'dashed 1px #000000';
   }
 
    function changeOld(){
     this.style.borderBottom = 'solid 3px #0099FF';
   }


    function doMyFunc(){
    var thePars = document.getElementsByTagName('p');
    
    for (var i=0; i<thePars.length; i++){
    thePars[i].onmouseover = changeNew;
    thePars[i].onmouseout = changeOld;
    
    }
   }
 
window.onload = doMyFunc;
 
</script>
<title>coffee</title>
</head>
<body>
 
 <p>test 1 </p>
 <p>test 2 </p>
 <p>test 3 </p>
 <p>test 4 </p>
 
</body>
</html>

I pass the value of the referred paragraph to a new function and it gets loaded in the new function with the “this” keyword.
Works okay.

But then I want to use setTimeout to delay the function.
How can I pass the value of the this keyword through that setTimeout ?

With this code it doesn’t work out:


 function firstChange(){
   setTimeout("changeNew",2000);
}

function secondChange(){
  setTimeout("changeOld()",3000);
} 

// the doMyFunc events are linked to these functions above right now

    function doMyFunc(){
    var thePars = document.getElementsByTagName('p');
    
    for (var i=0; i<thePars.length; i++){
    thePars[i].onmouseover = firstChange;
    thePars[i].onmouseout = secondChange;
    
    }
   }


Does someone have a good tutorial around this issue?

Great ! Works out super!
Thanks for the advice…

I have it like this right now to make cool buttons:


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
 
<script type="text/javascript">
function doEffect(){  
var obj;  
var TimerID; 
var interval = 100;
var thePars = document.getElementsByTagName('p');
 
    for (var i=0; i<thePars.length; i++){
    thePars[i].onmouseover = startFlip;
    thePars[i].onmouseout = stopFlip;
    }
 
 
function startFlip(){
 obj = this;
  TimerID = setTimeout(function(){styleOne(obj);},interval);
}
  
function styleOne(){
obj.style.border = 'solid 3px #0099FF';
obj.style.width = '100px';
obj.style.height = '49px';
  TimerID = setTimeout(function(){styleTwo(obj);},interval);
}
 
function styleTwo(){
obj.style.border = 'dashed 2px #669944';
obj.style.width = '100px';
obj.style.height = '50px';
  TimerID = setTimeout(function(){styleOne(obj);},interval);
}


function stopFlip(){
clearTimeout(TimerID);
obj.style.border = 'dashed 2px #669944';
obj.style.width = '100px';
obj.style.height = '50px';
}

 
}
 
window.onload = doEffect;
 
 
</script>
<title></title>
<style>
p { display: inline; width: 100px; height: 50px; border: solid 2px #0099FF; margin: 5px;
}
</style>
</head>
<body>
 
 
 <p>test 1 </p>
 <p>test 2 </p>
 <p>test 3 </p>
 <p>test 4 </p>


 
</body>
</html>

I’m on a computer with IE7 only at the moment.
Can’t verify other browsers right now.

The script only changes the style 1 time instead of 20 times like I hoped with this:
for(var j=0; j<20; j++){

That’s odd, what you posted appears to work for me in Chrome, Firefox and IE.

What seems to be the problem?

Ok, still don’t understand what I have to do (as a javascript noob)

I rewrote it all and have it like this now, but it still doesn’t work.
Do I still have to pass the interval like this aswel?



<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>

<script type="text/javascript">
function doEffect(){  
var obj;  
var TimerID; 
var TimerIDTwo;
var thePars = document.getElementsByTagName('p');

    for (var i=0; i<thePars.length; i++){
    thePars[i].onmouseover = doFlip;
    thePars[i].onmouseout = stopFlip;
    }



function doFlip(){
 obj = this;
  for(var j=0; j<20; j++){
  TimerID = setTimeout(function(){changeStyle(obj)},100);
  TimerIDTwo = setTimeout(function(){changeStyleTwo(obj)},500);
  return true;
  }
}


function stopFlip(){
 obj = this;
 TimerID = setTimeout(function(){changeStyleTwo(obj)},100);
}

function changeStyle(){
obj.style.borderBottom = 'solid 3px #0099FF';
}

function changeStyleTwo(){
obj.style.borderBottom = 'dashed 2px #669944';
}

}

window.onload = doEffect;


</script>
<title></title>
</head>
<body>

 
 <p>test 1 </p>
 <p>test 2 </p>
 <p>test 3 </p>
 <p>test 4 </p>
 
</body>
</html> 		 	   		  



Pardon, I misunderstood that the timeout event wasn’t working as expected.

One possible way to achieve it is to have each timeout function call the other timeout function.
When the mouseout event occurs you can then clear the timeout event and set the style to finish things off.

Yes, that’s a common problem that comes about from not quite understanding what’s occurring.

When the timeouts have been set up and the loops are completed, the timeouts run the appropriate functions which use the variables as they are at the time when those functions are run, not when they were set up.

If you want the variable from the loop to be retained, you will need to pass it to the function as well.

This means not just passing the this keyword to the function as context, but also passing interval to the function as interval.

Ok I see.

And if, for example, you want to let these 2 styles change a few times after each other, for creating a blinking effect. How do you put this in a loop?

It’s not repeating the loop with this one:


  function changeStyles(){

var interval = 500;
for (var t = 0; t<5; t++){

    setTimeout(function (context) {
        return function () {
            firstStyle.call(context);
        };
    }(this), interval);

    setTimeout(function (context) {
        return function () {
            secondStyle.call(context);
        };
    }(this), interval*2);    

  }
}  

As the timeout events run in a separate execution context, the this keyword will be set to the window object.

You can deal with that by passing the this keyword to a function that is called, so that you can then set that as the calling context.

Something like the following should work.


function firstChange(){
    setTimeout(function (context) {
        return function () {
            changeNew.call(context);
        };
    }(this), 2000);
}