views:

76

answers:

3

I'm just starting out learning javascript, and tried to write a little script that would make a grid of divs on a page.

Here's the script:

var tileWidth=50;
var tileHeight=100;
var leftPos=10;
var topPos=10;
var columns=10;
var rows=10;
var spacing=5;

$('document').ready(function() {
 placeTiles();
});

function makeRow() {
 for (var i=0; i<columns; i++) {
   $('#canvas').append('<div class="tile" style="left:' + leftPos + 'px;top:' + topPos + 'px;"></div>');
   var leftPos = leftPos + tileWidth + spacing;
 }
}

function placeTiles() {
 for (var i=0; i<rows; i++) {
  makeRow();
  var topPos = topPos + tileHeight + spacing;
 }
}

At the moment, 100 <div>s get created, all with a top position of 10px and a left position of undefined (for the first <div> in the row) or NaN.

What should I be doing differently? Why can't makerow() see my global leftPos variable (and all the other variables for that matter)?

Thanks.

+1  A: 

you declare leftPos to be of function scope with var leftPos, hiding the global declaration. If you're not declaring something don't use var

cobbal
But I'd already declared `leftPos` as a gloval var at the beginning of the script. I thought that I could then assign a local var with the same name within the function so that it would take the global var, add the other variables to it, and then overwrite it, so that all the other loops in the function would use the local var instead of the global. Then the next time the function was called it would reset back to the global var. So I can't do that?
Acorn
+1  A: 

"Why can't makerow() see my global leftPos variable (and all the other variables for that matter)?"

Because var is not a declaration. It is a function(scope)-wide annotation. In the top-level scope var essentially does nothing (the global execution context/scope is the window object) so it is the same as window.leftPos = 10 or just leftPos = 10. In the makeRow you essentially have:

function makeRow() {
 var leftPos = undefined // this is "hoisted" to the TOP of the function
 for (...) {
  // ...
  leftPos = leftPos + tileWidth + spacing
 }
}

Something look suspicious? :-)

Two solutions to this are 1) use a different variable name (recommended) 2) use the 'global' leftPos a property of the window object (as shown below).

Also, even though it is just a scope-wide annotation, it generally leads to more clear code if you keep the 'var's at the top (it is "hoisted" anyway, see above). Eg:

function makeRow() {
 var leftPos = window.leftPos // or use a different name, which is what I'd do
 for (var i=0; i<columns; i++) {
   $('#canvas').append('<div class="tile" style="left:' + leftPos + 'px;top:' + topPos + 'px;"></div>')
   leftPos = leftPos + tileWidth + spacing
 }
}

For more information, see: Identifier Resolution, Execution Contexts and Scope Chains

pst
Oh I think I see! When I do `var leftPos = leftPos + tileWidth + spacing`, I'm declaring a local variable leftPos, which overrides the global variable with the same name (actually what I wanted), but then when I try to assign the global variable to it, it has already been overwritten! So I can either refer to the global variable with the alternative method of window.leftPos, or give the global variable a different name. Thanks!
Acorn
@Acorn: Overwritten may not be the right term. The global variable would still exist, but since you had another local variable with the same name, the global one would not be accessible, unless you use `window.leftPost`.
Daniel Vassallo
Shadowed is the term I believe.
wombleton
+1  A: 

In JavaScript you can have function scope or global scope for your variables.

If you have two variables of the same name in different scopes, the first preference is given the variable declared in the function scope.

Therefore you have your leftPos and topPos variables declared in global scope at the top of the script, and you also have them declared within the makeRow() and placeTiles() functions. The var keyword is used for variable declaration, not for variable assignment.

You simply need to remove the variable declaration with var from within the functions, in order to use the global variables. Simply use:

leftPos = leftPos + tileWidth + spacing;

// and

topPos = topPos + tileHeight + spacing;

However, you should consider avoiding global variables altogether. Globals are evil.

Further reading:

Daniel Vassallo