admin管理员组

文章数量:1023744

I tried debugging my code for like a few hour but I got nothing out of it. The issue is that it makes absolutely no sense on why it reports an error every time I tried to use document.forms[0][i] (i as the iterator) in the event listener but "this" satisfies the code.

//broken
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(formFields[i]);
            });
        }
    }
};

//works
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(this);
            });
        }
    }
};

Wouldn't "this" refer to document.forms[0][i]?... formFields references to document.forms[0]. However the exact same code (with "this" where formFields[i] is at) works just fine.

Here is the demo: /

I tried debugging my code for like a few hour but I got nothing out of it. The issue is that it makes absolutely no sense on why it reports an error every time I tried to use document.forms[0][i] (i as the iterator) in the event listener but "this" satisfies the code.

//broken
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(formFields[i]);
            });
        }
    }
};

//works
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(this);
            });
        }
    }
};

Wouldn't "this" refer to document.forms[0][i]?... formFields references to document.forms[0]. However the exact same code (with "this" where formFields[i] is at) works just fine.

Here is the demo: http://jsfiddle/PbHwy/

Share Improve this question edited Jun 16, 2012 at 8:24 Codist asked Jun 16, 2012 at 8:13 CodistCodist 1,2082 gold badges12 silver badges29 bronze badges 2
  • Please post the exact error message. It also helps if you provide a little demo. You can use http://jsfiddle for this. – Zeta Commented Jun 16, 2012 at 8:17
  • When the onblur event occurs on the form field... Here is the error that pops up in the dev console "Uncaught TypeError: Cannot read property 'value' of undefined". – Codist Commented Jun 16, 2012 at 8:19
Add a ment  | 

3 Answers 3

Reset to default 3

Cranio's answer already contains the root of the matter. To get rid of this you can either include formFields[i] by using closures

var blurCallbackGenerator = function(element){
    return function () {
        checkNonEmpty(element);
    };
};
formFields[i].onblur = blurCallbackGenerator(formFields[i]);

/* // dense version:
formFields[i].onblur = (function(element){
    return function () {
        checkNonEmpty(element);
    };
})(formFields[i]);
*/

or simply using this.

See also:

  • MDN: Creating closures in loops: A mon mistake

Because you define formFields in a scope outside (or better, different than) the event listener. When the event listener is called, it is called not in the addListeners function where you define formFields, but "independently", so the reference is lost and its value is undefined (but this works because it is not dependent on that scope).

The problem is that the variable i (referred to in each of your handlers) is the exact same variable in each of them, which by the time the loop has finished has value formFields.length+1 and is therefore wrong for all of them. Try this instead [note: the below used to say something VERY WRONG before I edited it -- thanks to Zeta for pointing out my mistake]:

var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function(j) {
                return (function () {
                    checkNonEmpty(formFields[j]);
                })(i);
            });
        }
    }
};

and you'll find it works (unless there's another bug that I haven't noticed).

If you can afford to support only Javascript 1.7 and above, you can instead write your old code but make your for look like this: for (let i=0; i<formFields.length; i++). But you quite possibly can't.

I tried debugging my code for like a few hour but I got nothing out of it. The issue is that it makes absolutely no sense on why it reports an error every time I tried to use document.forms[0][i] (i as the iterator) in the event listener but "this" satisfies the code.

//broken
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(formFields[i]);
            });
        }
    }
};

//works
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(this);
            });
        }
    }
};

Wouldn't "this" refer to document.forms[0][i]?... formFields references to document.forms[0]. However the exact same code (with "this" where formFields[i] is at) works just fine.

Here is the demo: /

I tried debugging my code for like a few hour but I got nothing out of it. The issue is that it makes absolutely no sense on why it reports an error every time I tried to use document.forms[0][i] (i as the iterator) in the event listener but "this" satisfies the code.

//broken
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(formFields[i]);
            });
        }
    }
};

//works
var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function () {
                checkNonEmpty(this);
            });
        }
    }
};

Wouldn't "this" refer to document.forms[0][i]?... formFields references to document.forms[0]. However the exact same code (with "this" where formFields[i] is at) works just fine.

Here is the demo: http://jsfiddle/PbHwy/

Share Improve this question edited Jun 16, 2012 at 8:24 Codist asked Jun 16, 2012 at 8:13 CodistCodist 1,2082 gold badges12 silver badges29 bronze badges 2
  • Please post the exact error message. It also helps if you provide a little demo. You can use http://jsfiddle for this. – Zeta Commented Jun 16, 2012 at 8:17
  • When the onblur event occurs on the form field... Here is the error that pops up in the dev console "Uncaught TypeError: Cannot read property 'value' of undefined". – Codist Commented Jun 16, 2012 at 8:19
Add a ment  | 

3 Answers 3

Reset to default 3

Cranio's answer already contains the root of the matter. To get rid of this you can either include formFields[i] by using closures

var blurCallbackGenerator = function(element){
    return function () {
        checkNonEmpty(element);
    };
};
formFields[i].onblur = blurCallbackGenerator(formFields[i]);

/* // dense version:
formFields[i].onblur = (function(element){
    return function () {
        checkNonEmpty(element);
    };
})(formFields[i]);
*/

or simply using this.

See also:

  • MDN: Creating closures in loops: A mon mistake

Because you define formFields in a scope outside (or better, different than) the event listener. When the event listener is called, it is called not in the addListeners function where you define formFields, but "independently", so the reference is lost and its value is undefined (but this works because it is not dependent on that scope).

The problem is that the variable i (referred to in each of your handlers) is the exact same variable in each of them, which by the time the loop has finished has value formFields.length+1 and is therefore wrong for all of them. Try this instead [note: the below used to say something VERY WRONG before I edited it -- thanks to Zeta for pointing out my mistake]:

var addListeners = function() {
    var i;
    var formFields = document.forms[0];
    var formSubmit = formFields["submit"];

    for (i = 0; i < formFields.length; i++) {
        if (formFields[i] != formSubmit) {
            formFields[i].onblur = (function(j) {
                return (function () {
                    checkNonEmpty(formFields[j]);
                })(i);
            });
        }
    }
};

and you'll find it works (unless there's another bug that I haven't noticed).

If you can afford to support only Javascript 1.7 and above, you can instead write your old code but make your for look like this: for (let i=0; i<formFields.length; i++). But you quite possibly can't.

本文标签: