NaN
NaN

Reputation: 1306

How do I get a variable set in another scope from within a setTimeout?

I have a timer function I got off this site and have tried to modify it a bit to suit my needs. For the most part it works as I'd like but I'm not able to pass a parameter to the JavaScript function.

I want to use a select to change the id value, then pass that value into the JQuery load function. Can someone please show me how to do this?

It may be worth mentioning that when the the timer refreshes, the select value needs to "stick", it can't reset.

Here is what I have so far:

function RecurringTimer(callback, delay) {
  var timerId, start, remaining = delay;

  this.pause = function() {
    window.clearTimeout(timerId);
    remaining -= new Date() - start;
  };

  var resume = function() {
    start = new Date();
    timerId = window.setTimeout(function() {
      remaining = delay;
      resume();
      callback();
    },remaining);
  };

  this.resume = resume;
  this.resume();
}

var timer = new RecurringTimer(function() {
  $('#id').load('load.asp?id=1'); // The "id" should be "newVal" from JQuery below.
}, 5000);


$(document).ready(function(){
  $('#appList').live('change',function(){
    var select = $(this);
    var newVal = select.val();

    if(newVal != ''){

      // This is where the var newVal needs to be passed into the JavaScript.

    }
  });
});

Upvotes: 1

Views: 184

Answers (3)

Jared Farrish
Jared Farrish

Reputation: 49188

I've speculated to a degree here what you're up to, so there were some changes I made to accommodate just trying to make it work. So if you have any questions about why I did something, just let me know.

Here's my mock elements, trying to match what you had in your code:

<div id="selector">
    <select id="appList">
        <option value="1">Application List - App 1</option>
        <option value="2">Application List - App 2</option>
        <option value="3">Application List - App 3</option>
        <option value="4">Application List - App 4</option>
    </select>
</div>
<div id="id"></div>

​Note, #id is not a good identifier for an element; I almost renamed it to #loader several times. You might consider making this id attribute for this element more descriptive.

I did change the $.live to $.on; if the element you're working with in reality is like a select or something with a genuine change event, stick with $.on instead of $.live. $.live is deprecated and it probably won't be too many more versions before it's removed altogether.

I also switched out $.load for $.get so I could use the callback; jsFiddle doesn't really give me the option of working with $.load and returning anything. So I used $.get and substituted a log statement instead.

Ok, so what you'll see is a wrapping jQuery(); around all the RecurringTimer() function, as well as the callback, timer, and $.on. What this does is create a "closure scope" (think like it's enclosing that code it contains), so I'm no longer in window or global scope. I can access window by calling window, but this will belong to jQuery.ready(), which implies document.

Right at the top you'll see this:

jQuery(function run($){
    var $id = $('#id'),
        $applist = $('#appList'),
        timerId,
        start,
        remaining = 0,
        newval,
        select;

These are all private variables that will stay in scope for the function and callbacks that belong to this scope. So we can share them if I have them at this "top most private" scope. If they are called with var inside of the other functions in this scope, they will only belong to that scope. Like var select = $(this); is only accessible within that $.on handler.

Hence, I want var newval; at that top most private scope, so we can easily share it. You have to be careful, of course, since there are times you don't want to shove it all up to that scope (like var select).

Probably the rest of it is self-explanatory. You don't need window.setTimeout, just setTimeout. Most window-scoped properties and methods don't require you to prepend window. However, you may need to do so to disambiguate a local and global/window property, for instance if you had a local var location = 'http://...'; in scope. You would need window.location to get that in that case.

Let me know if you have any questions.

jQuery(function run($){
    var $id = $('#id'),
        $applist = $('#appList'),
        timerId,
        start,
        remaining = 0,
        newval,
        select;

    $applist.on('change', function(){
        var select = $(this);

        $id.prepend('<p>App List change event fired</p>');

        newval = select.val();

        if (newval != '') {
            var timer = new RecurringTimer(function() {
                $.get('/echo/html/?id=' + newval, callback); 
            }, 5000);
        }

        function callback(msg, status){
            var report = '<p>Callback returned ' + status + 
                         ' for '  + this.url +  '.</p>';

            $id.prepend(report);
        }
    });

    function RecurringTimer(callback, delay) {
        remaining = delay;

        $id.prepend('<p>RecurringTimer initialized.</p>');

        this.pause = function() {
            clearTimeout(timerId);

            remaining -= new Date() - start;
        };

        var resume = function() {
            start = new Date();

            timerId = setTimeout(function() {
                remaining = delay;
                resume();
                callback();
            }, remaining);
        };

        this.resume = resume;

        this.resume();
    }
});

http://jsfiddle.net/userdude/zU5b3/

There's also some CSS, but I don't think it's relevant, so I left it out.

Upvotes: 1

Hazem Salama
Hazem Salama

Reputation: 15101

Try this

function RecurringTimer(callback, delay) {
 var timerId, start, remaining = delay;
 var newValue;
  .....
  .....
  .....

and then in your jquery

if(newVal != ''){

     newValue = newVal;

}

Then you can reference it inside your timer

    var timer = new RecurringTimer(function() {

        $('#id').load('load.asp?id='+newValue); // The "id" should be "newVal" from JQuery below.
}, 5000);

Upvotes: 0

techfoobar
techfoobar

Reputation: 66663

Well, you could do:

if(newVal != ''){
    window.newVal = newVal;
}

And use window.newVal where needed:

$('#id').load('load.asp?id=' + window.newVal);

Upvotes: 0

Related Questions