SitePoint Sponsor

User Tag List

Results 1 to 18 of 18
  1. #1
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    Odd Javascript 'for var in' problem

    Hi,

    This is my current code (stripped down considerably btw):

    Code:
    // function to initialise the login stuff
    function initLogin () {
    	var login = ["login-username", "login-password"];
    	for ( var i in login ) {
    		alert(login[i]);
    	} 
    }
    Very simple, create an array, and then loop through each item, afaik I've done it right.

    Problem is, it alerts the first two items just fine, then it suddenly starts going mad and alerts a whole load of random functions, for example:



    I have no idea why it does this, I've tried pop'ing the array, but all that does is remove one of my genuine array values and still outputs the random functions.

  2. #2
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Oh my life, please ignore - sorry. Someone else working on the project had included mootools without me realising and was causing the error, took me ages to work that out ^_^

  3. #3
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    207
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Code javascript:
    function initLogin () {
    	var login = ["login-username", "login-password"], i;
    	for ( i in login ) {
    		if (login.hasOwnProperty(i))
    			alert(login[i]);
    	} 
    }

    http://yuiblog.com/blog/2006/09/26/for-in-intrigue/

    Curious question: are you using a javascript library?

    Edit; heh, i thought so.
    mmj

  4. #4
    SitePoint Wizard
    Join Date
    Nov 2004
    Location
    Nelson BC
    Posts
    2,310
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Your variable login is an array, so you will at least get the .length property output as well.

    For..in isn't the best way to iterate arrays because it returns all the properties in addition to the array elements.

  5. #5
    SitePoint Addict
    Join Date
    Dec 2007
    Posts
    207
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by jimfraser View Post
    Your variable login is an array, so you will at least get the .length property output as well.

    For..in isn't the best way to iterate arrays because it returns all the properties in addition to the array elements.
    Yep.
    mmj

  6. #6
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Cheers guys Good job I posted here anyway, I'll change it to reflect the length property now.

  7. #7
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by jimfraser View Post
    Your variable login is an array, so you will at least get the .length property output as well.
    I'm pretty sure, that for .. in ignores .length? May be implementation-specific.

  8. #8
    SitePoint Wizard
    Join Date
    Nov 2004
    Location
    Nelson BC
    Posts
    2,310
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I was 99% sure of this but I just tested it and it doesn't return the length property.

    If you use Array.prototype to define new methods, it will return those. Maybe that's what was confusing me.

  9. #9
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    The for...in statement is supposed to be used for objects. Troubles can occur when using it for arrays.
    http://developer.mozilla.org/en/docs.....in_Statement

    mmj has already shown how to use for...in properly with objects.
    If you want to loop through an array, the proper way it to use a normal loop, or array.foreach(fn) if you can support javascript 1.6
    http://developer.mozilla.org/en/docs...ng_over_arrays

    Code javascript:
    var arrayLen = array.length,
        i,
        value;
    for (i = 0; i < arrayLen; i += 1) {
        value = array[i];
        ...
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  10. #10
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Many thanks for the info and apologies for such a late reply, I haven't touched this project for a little while and kinda forgot about it! I've changed it now to something that's hopefully a bit better, but I'm still not having an awful lot of luck with it

    Code JAVASCRIPT:
    function initLogin () {
    	var login = ["login-username", "login-password"];
    	for (i = 0; i < login.length; i++) {
    		elem = document.getElementById(login[i]);
    		elem.onfocus = function () {
    			if(elem.value == elem.defaultValue) {
    				elem.value = "";
    				elem.className = "active";
    			}
    		};
    		elem.onblur = function () {
    			if(elem.value == '') {
    				elem.value = elem.defaultValue;
    				elem.className = "inactive";
    			}
    		}
    	}
    }
    It seems to be fine, but the problem is, when I click on the first element, it's executing the code for the second element if that makes sense? As if the one is over-writing the other.

  11. #11
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    A quick test using: alert(elem.value);

    Alerts the values no problem, so I assume it is declaring elem fine.

  12. #12
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Since you don't have var in front of the first use of elem, you're implicitly using a global variable, which is overwritten on each iteration of the loop.

  13. #13
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Ah! Good catch - thank you.

    It's still doing it for some reason, so I can only assume I've screwed up somewhere else too, d'oh.

  14. #14
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Also, when I define each elem seperately it does the same thing:

    Code JAVASCRIPT:
    		var elem = document.getElementById("login-username");
    		elem.onfocus = function () {
    			if(elem.value == elem.defaultValue) {
    				elem.value = "";
    				elem.className = "active";
    			}
    		};
    		elem.onblur = function () {
    			if(elem.value == '') {
    				elem.value = elem.defaultValue;
    				elem.className = "inactive";
    			}
    		}
     
    		var elem = document.getElementById("login-password");
    		elem.onfocus = function () {
    			if(elem.value == elem.defaultValue) {
    				elem.value = "";
    				elem.className = "active";
    			}
    		};
    		elem.onblur = function () {
    			if(elem.value == '') {
    				elem.value = elem.defaultValue;
    				elem.className = "inactive";
    			}
    		}
    But if I change the elem name in the second chunk of code it works just fine. So I guess for some reason it still doesn't like both having the same variable name?

  15. #15
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, you can't declare a variable twice within the same scope. var relates to the scope, not the sequence. So in this case, you will have to pick a different name in the second case.

    Actually, come to think of it, that goes for the loop as well. elem will be rebound on each iteration, so the closure won't work as expected. Try using an inner function:
    Code javascript:
    initLogin = function() {
      var bindEventHandlers = function(elem) {
        elem.onfocus = function() {
          if (elem.value == elem.defaultValue) {
            elem.value = "";
            elem.className = "active";
          }
        };
        elem.onblur = function() {
          if (elem.value == '') {
            elem.value = elem.defaultValue;
            elem.className = "inactive";
          }
        };
      };
      var login = ["login-username", "login-password"];
      for (var i = 0; i < login.length; i++) {
        bindEventHandlers(document.getElementById(login[i]));
      }
    };

    Inside the loop, you call bindEventHandlers. Each time the function is called, a new scope is created, so that the variables don't overlap each other.

  16. #16
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Another solution is to ignore the elem variable for the event code itself.

    You can use the this keyword that points to the element that fired the event.

    Code javascript:
    function initLogin () {
        var login = ["login-username", "login-password"];
        for (i = 0; i < login.length; i++) {
            elem = document.getElementById(login[i]);
            elem.onfocus = function () {
                if(this.value == this.defaultValue) {
                    this.value = "";
                    this.className = "active";
                }
            };
            elem.onblur = function () {
                if(this.value == '') {
                    this.value = this.defaultValue;
                    this.className = "inactive";
                }
            }
        }
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  17. #17
    SitePoint Addict
    Join Date
    Jun 2007
    Posts
    396
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Many thanks for the great solutions and explainations guys, really appreciate it - it's working an absolute treat now.

    Thanks once again

  18. #18
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken View Post
    Actually, come to think of it, that goes for the loop as well. elem will be rebound on each iteration, so the closure won't work as expected. Try using an inner function:
    the more common approach in javascript is to use iterators instead of loops, i.e.

    Code:
    each(["login-username", "login-password"], function(elem) {
      // elem is bound here
    });


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •