3

I'm converting some JavaScript code to jQuery and have come to a halt, Heres the code...
HTML:

<div id="text"></div>

JavaScript:

keywords = [ "one", "two", "three"]
var allowed = true;
function ChangeText ( ) {
   if(allowed)
   {
      var keyword = keywords[ Math.floor( Math.random() * keywords.length ) ]
      document.getElementById( "text" ).innerHTML = keyword;
   }
   setTimeout( "ChangeText()", switchTime );
} 
ChangeText();

jQuery:

var changeAllowed = true;
$(document).ready(function ChangeText() {
   if(changeAllowed)
   {
      $("#text").fadeOut(500);
      var keyword = keywords[ Math.floor( Math.random() * keywords.length ) ];
      $("#text").text(keyword).fadeIn(1000);
   };
   setTimeout( "ChangeText()", 2000 );
});
ChangeText();

What it should do is fade the text out then back in with a new string (which it does on refresh) every 2 seconds or so, The jQuery effects are working, it just seems to be the setTimeout() or I've not named the function properly.

Squiggles
  • 45
  • 1
  • 4
  • 1
    Where is `switchTime` defined? – Briguy37 Jul 20 '11 at 18:10
  • Your code will actually probably work in IE8 and below, because you're using a *named function expression* and IE leaks the name into the enclosing variable environment. Fixed in IE9 I believe. – user113716 Jul 20 '11 at 18:21

4 Answers4

7

Passing a string to setTimeout will evaluate from the global scope, in which the funcion is not defined. Pass it directly instead, so that it will evaluate from the function's scope, in which the function (obviously) is defined:

setTimeout( ChangeText, 2000 );
pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • Technically, the function is defined in the global scope (unless there's another function wrapping all this that we can't see). But with a named function expression, the name itself will only be available inside the function. – user113716 Jul 20 '11 at 18:25
  • @patrick dw: Thanks for your explanation. However if it's defined in the global scope how can we access it? – pimvdb Jul 20 '11 at 18:27
  • 1
    There isn't a named reference retained in the global scope, even though it's being defined there. – user113716 Jul 20 '11 at 18:30
4
setTimeout( arguments.callee, 2000 );

arguments.callee is the "self pointer" for functions. Biggest advantage is that it works with anonymous functions and named functions alike. So, for jQuery:

var changeAllowed = true;
var keywords      = [anything];

$(document).ready(function () {
   if(changeAllowed) {
      var keyword = keywords[ Math.floor( Math.random() * keywords.length ) ];
      $("#text").fadeOut(500).text(keyword).fadeIn(1000);
   };
   setTimeout(arguments.callee, 2000 );
});

Note: This answer is from 2008 originally. Nowadays the proper way to do this has changed. As noted in the comments you'd use a named function expression (NFE), like this:

var changeAllowed = true;
var keywords      = [anything];

$(document).ready(function fadeKeyword() {
   if(changeAllowed) {
      var keyword = keywords[ Math.floor( Math.random() * keywords.length ) ];
      $("#text").fadeOut(500).text(keyword).fadeIn(1000);
   };
   setTimeout(fadeKeyword, 2000 );
});

arguments.callee will still work in non-strict mode but cause an error in strict more.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 1
    Keeping in mind that `arguments.callee` isn't valid in *strict mode*. :o) – user113716 Jul 20 '11 at 18:19
  • 2
    @Tomalak: Nothing, unless you consider a named function expression to be an acceptable replacement. OP's code is using a NFE, which means that the name is only available *inside* the function (except in IE8 and below, where it leaks). – user113716 Jul 20 '11 at 18:23
  • 2
    Ways to avoid `arguments.callee` and why you should if you can: http://stackoverflow.com/questions/103598/why-was-the-arguments-callee-caller-property-deprecated-in-javascript – Ray Toal Jul 20 '11 at 18:34
  • arguments.callee works, however it seems to be running parallel to the fadeOut/fadeIn timers, e.g. fade out > fade in >change instead of fade out > change > fade in adding a delay or changing the timeout time is tacky – Squiggles Jul 20 '11 at 19:17
  • @aprice: This is a different problem and it's unrelated to `arguments.callee`. Read in the jQuery docs how animation effects like `fade()` work and how to chain them. – Tomalak Jul 20 '11 at 20:08
3

Use it without parens and quotes: setTimeout(ChangeText, 2000 );

Mrchief
  • 75,126
  • 20
  • 142
  • 189
2

UPDATE

My first solution got out of synch when the display queue got behind. Here is updated code that uses callbacks so the timing is always right, along with the test fiddle:

var changeAllowed = true;
var keywords = ["test1", "test2", "test3", "test4", "test5"];
var hide = true;

var toggleTextFade = function() {
    if(changeAllowed) {
        var keyword =keywords[Math.floor(Math.random() * keywords.length)];

        $("#text").text(keyword).fadeIn(1000,null,function(){
            $("#text").fadeOut(500,null,toggleTextFade)});
    }               
};


$(document).ready(toggleTextFade);
Briguy37
  • 8,342
  • 3
  • 33
  • 53