1

This is a scope issue, correct?

EDIT: http://jsfiddle.net/RPwLK/7/

On line 38 of the following function, where displayBossMessage() is called by itself, the variable "boss" seems to be out of scope. It should contain the name (string) of the current boss, and when the function is called, it should put it back in the "boss" argument.

But boss seems to always be undefined. I've tried creating a variable right before the jQuery listener is created to see if it would be within scope, but it seems as if it isn't.

Or maybe I'm just having a slow day. ;p

function displayBossMessage(boss,message,options,timer){
    boss = bosses[boss];

    //clear any possible lingering text/buttons
    $(boss.messagebox).text('');
    $(boss.optionsbox).text('');
    displayMessage_CurrentBoss = boss;

    //if no options provided, set a default "continue" button
    if(options == ''){
        options = {
            'default' : {
                'text' : 'Continue',
                'func' : function(){}
            }
        }
    }

    $('#container div').hide();
    $(boss.div).fadeIn(1500);

    writeMessage(message,$(boss.messagebox),0);

    setTimeout(function(){
        $(boss.optionsbox).fadeIn(1000);
    },3000);

    //"listen" for a choice
    var i = 0;
    for(option in options){
        $(boss.optionsbox).html($(boss.optionsbox).html() + '<button name="'+ i +'">'+ options[option].text +'</button> &nbsp;');

        $(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
            options[option].func();

            //close message screen or show defined response
            if(typeof options[option].response != 'undefined'){
                displayBossMessage(boss,options[option].response,'',true);
            }else{
                $(boss.div).hide();
                $('#container div').fadeIn(1500);
            }

        });
    }

    if(timer){
        //if they are afk/don't click after a minute, do it for them
        setTimeout(function(){
            $(boss.div+' button[name="0"]').click();
        },60000);
    }
}

Hope I'm not being completely oblivious and missing something so simple.

*Edit: Bosses variable (is global) *

1 Answers1

1

(updated jsfiddle revision link to #11 which includes both solutions)

Looks like this could be a working fiddle: http://jsfiddle.net/RPwLK/11/

A minor problem: you have an extra ' on line 30 with the (second) alert call - the string literal was not closed correctly (or rather another was being opened). After that I was able to investigate and come up with the following conclusion (2 problems)...


The first problem was with the variable override here:

function displayBossMessage(boss,message,options,timer){
    boss = bosses[boss]; // this line, boss was a string, now it will be an object

And the later usage in the same function here:

if(typeof options[option].response != 'undefined'){
    displayBossMessage(boss,options[option].response,'',true); // first arg is now an object

The solution is to create a reference to the original boss when it was a string like:

function displayBossMessage(boss,message,options,timer){
    var origBoss = boss; // let's remember what it was in its original string form
    boss = bosses[boss];

And use it like so:

if(typeof options[option].response != 'undefined'){
    displayBossMessage(origBoss,options[option].response,'',true); // now we're dealing with a string ref

The second problem is the reference to option within the for loop. It was always referencing the last value since the $(document).on('click'... is always delayed (asynchronous). There are a number of ways to solve this. I chose to use bind and pass in an argument with a reference to the value of option for each specific iteration.

Notice that in the original option is in the async function but not in a closure (for is not a closure):

for(option in options){
    //...
    $(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
        options[option].func(); // here option is always the last item in options

So introduce an argument conveniently called option in the callback function for the click handler:

$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){ // here
    options[option].func(); // and now option is whatever the argument value is

And don't forget to pass it in the function declaration via bind:

    $(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){
        options[option].func();
        // ...
    }.bind(this,option)); // here we're passing whatever option is for the specific iteration as the first argument of the callback function

Note, this is just the scope, and each subsequent parameter for bind (see MDN) after the first corresponds to the arguments in the function definition.

zamnuts
  • 9,492
  • 3
  • 39
  • 46
  • Aah! Thank you. I'm such an idiot sometimes. Also, sorry for originally supplying the wrong link. Thanks again! – user3029571 Nov 28 '13 at 04:13
  • @user3029571 np, it happens, i updated the question with the solution to a secondary problem - bonus! – zamnuts Nov 28 '13 at 04:16
  • Wow, I did not see that one coming. You probably saved me an hour or two of trying to figure out what was going on. I appreciate it, again! – user3029571 Nov 28 '13 at 04:40
  • @user3029571 yup! there are some other problems too (lots of alerts), but i'll leave those for you (or perhaps another SO question). also, be weary of `bind` since its not supported in all browsers, [here are some alternatives (SO question)](http://stackoverflow.com/questions/20022712/javascript-ie8-alternative-to-bind) *oh and once you get 15 rep, an upvote for a +10 would be cool, thanks!* – zamnuts Nov 28 '13 at 05:26
  • I will most definitely come back and vote you up. :) – user3029571 Nov 28 '13 at 09:25