1

I want to introduce a simple notation to bind a HTML <input> element to a JavaScript object. Something like:

<script>            
    var qform = {
        uid : "",
        pass : ""
    };
</script>

<form method="get" action="#">
    <input id="temp0" type="text" name="uid" bind="qform.uid" />
    <input id="temp1" type="password" name="pass" bind="qform.pass" />
    <input type="submit" value="submit" />
</form>

So that any changes to the <input>s will change my JS variable. The way I'm trying to implement it is:

<script>

    var x = 0;
    for(x = 0; x < 2; x++) {

        var inputField = document.getElementById("temp" + x);


        var bindObj = inputField.getAttribute("bind");            

        var bindObjTree = bindObj.split(".");
        var parent = window;
        for (var i = 0; i < bindObjTree.length - 1; i++) {
            parent = parent[bindObjTree[i]];
        }
        child = bindObjTree[bindObjTree.length - 1];

        inputField.value = parent[child];

        inputField.onchange = function() {
            var xp = parent;
            var xc = child;
            xp[xc] = inputField.value;
            alert(JSON.stringify(window["qform"]));
        };

    } // for

    </script>

However only the second input field behaves the way I want to. Can someone explain why that is? I'm guessing it has something to do with closures. I'm really trying to understand what I'm doing wrong rather than find a solution (I can easily work around this with JQuery, but I don't really want that).

tinkerbeast
  • 1,707
  • 1
  • 20
  • 42
  • 1
    bind is not a valid attribute name. use data-bind instead. – Konstantin Dinev Jan 08 '13 at 08:11
  • In your case, why aren't you using `eval` to get the object reference that you want to bind? The way you doing is very harder and in this case has less performance. Do you have some restriction with `eval`? – Gabriel Gartz Jan 08 '13 at 08:18
  • @GabrielGartz historically I have avoided eval, and going forward with the code I won't be able to use eval. However with the above example, I do agree that eval is the better choice – tinkerbeast Jan 08 '13 at 08:58
  • Many developers recommend against using eval(). Before using eval as part of your solution, you should consider any damage that might be done if an 'attacker' changed your attribute values to include malicious code which it then relies on your program to run via an eval statement. – Jared Clemence Mar 22 '16 at 01:49
  • This problem is similar to https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example where the concepts are better explained. – tinkerbeast Nov 02 '19 at 18:26

3 Answers3

1

The issue is with these:

        parent = parent[bindObjTree[i]];
        child = bindObjTree[bindObjTree.length - 1];

and

    inputField.onchange = function() {
        var xp = parent; // Always refers to the element retrieved at index 1 of the for loop
        var xc = child; // Always refers to the element retrieved at index 1 of the for loop
        // This is regardless of which input's event handler executes
        xp[xc] = inputField.value;
        alert(JSON.stringify(window["qform"]));
    };

These will always refer to the elements found in the last iteration of the for loop because of closure.

Konstantin Dinev
  • 34,219
  • 14
  • 75
  • 100
0

inputFields needs to be an an array so it doesn't get overwritten in the loop or

you can place an id in the form tag and instead reference it to get the fields as its child elements.

Quentin Engles
  • 2,744
  • 1
  • 20
  • 33
0

Your child variable is global. Try to use:

var child = bindObjTree[bindObjTree.length - 1];

instead. Since it is global, you will overwrite the global child variable on the second turn.

Chris
  • 7,675
  • 8
  • 51
  • 101