0

The variable cont is being lost in the following:

    __factory.setupMenu = function(cont,input,multiSelect,exclusive,popMenu){               
        var __menu = {multiSelect:multiSelect};
        spotter.events.setEventTrigger(input,'change');
        __menu.exclusive = {inputs:[],values:exclusive||[],simpleValues:[]};
        alert(cont);//<-- is defined here
        window.popSelectComponent= cont;//<-- saved it globally to test reference

        return function(ajaxResult){
            var data = ajaxResult.template.response||[];
            var info = {},l=data.length;
            while(--l > -1){
                info[String(data[l].value)] = data[l].abbr||data[l].name;
            }

            var textTarget;
            alert(window.popSelectComponent);//<-- this is defined as expected
            alert(cont);//<-- is now undefined
            alert(input);//<-- this is defined as expected
            if(!(textTarget = cont.querySelector('[data-pop-selected]'))){textTarget = cont;}

if(!input.popSelectTemplate){   
                spotter.data.bindElementToInput(textTarget,input,function(content){
                    content = content.split(',');
                    var l=content.length;
                    while(--l > -1){
                        content[l] = info[content[l]];
                    }
                    content = content.join(',');
                    return (content.length ? content : 'ignore');
                });
            }
            else{
                var cont = document.createElement('SPAN');//<-- PROBLEM IS CAUSED HERE. HOISTING IS CAUSING CONT TO BE UNDEFINED AT CLOSURE START
                cont.className="multi-select";
                cont.appendChild(cont);

                //removal function
                var remove = (function(input){
                    return function(e){
                        var evt = e ? e:window.event;
                        if (evt.stopPropagation)    evt.stopPropagation();
                        if (evt.cancelBubble!=null) evt.cancelBubble = true;
                        if(input.value !== input.spotterPopSelectDefaultValue){ 
                            input.value = input.value.removeListValue(this.getAttribute('data-id'),',');
                            spotter.deleteElement(this);
                            if(input.value === '' && input.value !== input.spotterPopSelectDefaultValue){
                                input.value = input.spotterPopSelectDefaultValue;
                                input.eventTriggers['pop-select-change']();
                            }
                        }
                    };
                }(input));

                input.spotterPopMenuOptions = __menu;
                input.addEventListener('pop-select-change',(function(cont, info, template){ 
                    return function(){
                        var HTML = '';
                        this.value.split(',').forEach(function(val){
                            HTML += template.replace('$[ID]', val).replace('$[NAME]', info[val]);
                        });
                        cont.innerHTML = HTML;
                        spotter.castToArray(cont.children).forEach(function(el){ console.log('option el',el); el.addEventListener('click',remove,false); });

                        console.log('input.spotterPopMenuOptions',input.spotterPopMenuOptions);
                    }; 
                }(cont, info, input.popSelectTemplate.innerHTML)),false);
            }
....

So running var func = __factory.setupMenu(...)({template:{}}) I am receiving an error message that cont is undefined while window.popSelectComponent is defined like expected. I tried changing the name of cont thinking I was overlooking something that was changing the value but that did not work either.

After running the function, I check cont in the context that initially created this closure and cont is still defined so it is not a matter of an object reference being lost as far as I can tell.

Magic Lasso
  • 1,504
  • 5
  • 22
  • 29
  • Try `console.log(...)` instead of using alert. It will make your life easier and you won't regret it. – Robert May 08 '16 at 05:46
  • Does your code *actually* have `alert(cont)` inside that function or did you try running that in the console while debugging. I suspect there's something like this going on: http://stackoverflow.com/a/21918024/1715579 – p.s.w.g May 08 '16 at 05:47
  • I only added alert to test for value because of the huge number of console.log messages i currently am running :P – Magic Lasso May 08 '16 at 05:47
  • @p.s.w.g It's not like that because he **does** use `cont` in the last line of the closure. – Barmar May 08 '16 at 05:48
  • 2
    This looks like a tricky one. Can you make a jsfiddle or give a link to the live site? – Barmar May 08 '16 at 05:49
  • I found the issue. Im guessing something to do with hoisting that I dont really know about. I set a new var cont... later on in the closure. Once I changed the variable name, it is working fine. This was some code from a big library and I renamed variables for easier reading, this is where the conflict came from. – Magic Lasso May 08 '16 at 05:58

1 Answers1

0

Perhaps a highly simplified example would make the problem more obvious:

var outer = function(theVariable) {
  console.log("In the outer function, theVariable is", theVariable);
  var inner = function() {
    console.log("In the inner function, theVariable is", theVariable);
    if (false) {
      var theVariable = 2;
    }
  };
  inner();
}
outer(1)
In the outer function, theVariable is 1
In the inner function, theVariable is undefined

As you have seen, the fact that a different variable with the same name has been declared (even though not initialized) in the inner function hides the properly initialized variable in the outer function that would have otherwise been visible.

You might think that because the variable is declared in a block, it would not affect other parts of the function. No, var is function scoped, not block scope.

This flaw has been addressed in modern versions of Javascript, and the var keyword has been superseded by let, which has the block scope you were expecting. var is kept for backwards compatibility, but you should not use it in new code.

Michael Lorton
  • 43,060
  • 26
  • 103
  • 144
  • I doubt that he thought the block was relevant, but merely that the `var` statement was executed after `console.log`. Hoisting makes the position of the `var` declaration irrelevant. – Barmar May 17 '16 at 21:48