0

I am very new to javascripts and trying to create a dynamic html form where there are multiple button, and each button click map to a corresponding form input. Here is my code:

<html>
<head>
    <title>Create Group</title>
    <script src="/js/jquery-3.4.1.min.js"></script>

    <script>

        $(document).ready(function(){

            $("#generate_form").click(function(){
            var number = document.getElementById("number_of_groups").value;
            var container = document.getElementById("container");
            while (container.hasChildNodes()) {
                container.removeChild(container.lastChild);
            }
            var i;
            for (i=1;i<=number;i++){
                var p = document.createElement("p");
                var node = document.createTextNode("group " + i + " :");
                p.appendChild(node);
                container.appendChild(p);
                var input = document.createElement("input");
                input.type = "text";
                var thisID = 'group_'+i;
                input.id = thisID;
                input.name=thisID;
                container.appendChild(input);
                var button = document.createElement("button");
                button.id = "button_"+i;
                button.type = "button";
                container.appendChild(button);
                button.onclick = function(){ document.getElementById(thisID).value = "hello world";};
                var buttonLabel = document.createTextNode("Generate");
                button.appendChild(buttonLabel);
                container.appendChild(document.createElement("br"));
            }
           })
        });
    </script>
</head>
<body>
    <h2>Create some group(s)</h2>
    <br>
    Create <input type="text" id="number_of_groups" name="number_of_groups" value="1"> group(s).
    <button id="generate_form" type="button">GO</button>
    <div id="container"/>

</body>
</html>`

So, the user would input number of groups to create and click 'Go' button, then the code should dynamically generate the form with the number the user choose. Each group of the form includes a input textbox and a 'Generate' button. When the button is clicked, the input textbox will show "hello world". However, the "hello world" only show up in the last input textbox no matter which 'Generate' button I click. So I changed the onclick function of the button to:

button.onclick = function(){ alert(thisID);};

Then I found that thisID is always the id of the last input textbox no matter which 'Generate' button I click. I guess that is because the binding of the click event does not happen till the script is done when 'thisID' would always be its latest value.

Would anyone please help me to realize the functionality I want? Thank you very much!

dangwh
  • 23
  • 3
  • Instead of using `var` everyone, try using `let`, and see if that fixes it for you. ie. `let thisID = 'group_'+i;` – Blue Oct 14 '19 at 18:26
  • Thank you very much! That actually fixed the problem. I will go take a look at the difference of `let` and `var`. – dangwh Oct 14 '19 at 18:29
  • Possible duplicate of [How do JavaScript closures work?](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Blue Oct 14 '19 at 18:46
  • Thanks again! That's very useful information. – dangwh Oct 14 '19 at 19:07

2 Answers2

0

You would need to wrap the code within the for loop in a separate function, passing in the value of i as a parameter. This would create a closure, creating a new execution scope for your code. Otherwise what is happening is that your var is being hoisted, and is not exclusive to each iteration of the for loop, so your DOM is reflecting only the last value it was assigned.

for (i=1;i<=number;i++){
  (function (i) {
    var p = document.createElement("p");
    var node = document.createTextNode("group " + i + " :");
    p.appendChild(node);
    container.appendChild(p);
    var input = document.createElement("input");
    input.type = "text";
    var thisID = 'group_'+i;
    input.id = thisID;
    input.name=thisID;
    container.appendChild(input);
    var button = document.createElement("button");
    button.id = "button_"+i;
    button.type = "button";
    container.appendChild(button);
    button.onclick = function(){ document.getElementById(thisID).value = "hello world";};
    var buttonLabel = document.createTextNode("Generate");
    button.appendChild(buttonLabel);
    container.appendChild(document.createElement("br"));
  })(i);
}

You can check out an article on closures here: https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36

EDIT: As one of your commenters mentioned, you can also set your vars to 'let' to achieve a similar effect. This is because let scopes the variable to the current code block, rather than being hoisted to the scope of the function, so each for loop iteration has a private let variable. It is still recommended to get a good understanding of closures and how they work, however.

Thomas Preston
  • 697
  • 1
  • 7
  • 19
0

Since you are already using JQuery, you can reduce some of the logic. Let me know if this helps-

<html>
    <head>
        <title>Create Group</title>
    </head>
    <script src="/js/jquery-3.4.1.min.js"></script>
    <script>
        $(document).ready(()=>{

        var txtGroup='<input type="text" id="txt_group_{0}" value="">';
        var btnGroup='<button id="btn_group_{0}" type="button">Click Me</button>';
        var container=$('#container');

        $('#generate_form').click((e)=>{
            var groupCount=parseInt($('#number_of_groups').val());
            var idToStart=$('#container').children('div').length+1;
            for(let i=idToStart;i< idToStart+groupCount;i++){
                var divGroup=`<div id="div_group_${i}">`+
                txtGroup.replace('{0}',i)+
                btnGroup.replace('{0}',i)+`</div>`;

                container.append(divGroup);
                $('#btn_group_'+i).on('click',(e)=>{
                    console.log('#txt_group_'+i);
                    $('#txt_group_'+i).val('Hello World');
                });
            }

        });

    });
    </script>
    <body>
        <h2></h2>
        <br>
        Create <input type="text" id="number_of_groups" name="number_of_groups" value="1"> group(s).
        <button id="generate_form" type="button">GO</button>
        <div id="container"></div>
    </body>
    </html>
kode_anil
  • 66
  • 1
  • 6
  • Thank you very much! This has opened my eye of writing javascripts. I still don't understand the difference between jquery and javascripts yet. Will do some reading. – dangwh Oct 16 '19 at 19:45
  • Happy to help. I am sure you will get tons of information on Javascript and JQuery :). In short, JQuery is a javascript library, we use JS libraries to achieve functionality much simpler(fewer lines of code) way than you would in vanilla JS. However in order to be confident web developer understanding vanilla JS is important, and you can understand what JQuery is doing under the hood. – kode_anil Oct 17 '19 at 18:48