How to access an underlying element's ID inside of a javascript clone?

Viewed 407

I'm creating a form. In the input section, I'm using a table to organize the input lines. When the pages loads, currently one line is being shown. If the user needs additional lines, then the user clicks the button labeled additional and it clones the div. I've been able to change the id of each additional div that gets created, but I need to also change the id of the underlying elements. This is the html portion:

<table id="input_values">
    <tr id="addtl_country_0">
        <td>
            <select name="countryToVisit" id="countryToVisit" required="">
                <option selected disabled value="select-country">Select Country</option>
                <option value="uk">United Kingdom</option>
                <option value="germany">Germany</option>
                <option value="chinahk">China/Hong Kong</option>
            </select>
        </td>
    </tr>
</table>

So far for the JavaScript portion, I have:

let countriesCounter = 1;

function addtlCountry() {
    let addtlRow = document.getElementById('addtl_country_0');
    let table = document.getElementById('input_values');
    let clone = addtlRow.cloneNode(true);
    clone.id = "addtl_country_" + countriesCounter;
    table.appendChild(clone);

    countriesCounter++;
}

With the javascript I currently have, each additional div id changes: addtl_country_0 => addtl_country_1. I would like to be able to do something similar with the select ID ie, "countryToVisit" => "countryToVisit_1".

I'm also open to hearing if there are any better ways of doing this. The "output" of this input is to create a static table that displays the chosen country in the first column, subsequent columns will depend on that country.

2 Answers

You can get the select element from your clone object and then update it's id. Here is sample code, you can extend this to make as a generic fn

function addtlCountry() {
    let addtlRow = document.getElementById('addtl_country_0');
    let table = document.getElementById('input_values');
    let clone = addtlRow.cloneNode(true);
    var select = clone.getElementsByTagName('select');
    select[0].id = select[0].id + "_1";
    table.appendChild(clone);    
    updateId(clone.childNodes);
    
    countriesCounter++;
}

Other option is instead of cloning the existing object you can have a function to generate the required html for you. Here you will be able to set the ids as you wish. This will ofcourse depend on your use case. If you know the form element is going to be select and you also have the data then constructing the html yourself makes sense. If it's completely dynamic and you don't have control on what form controls will be there, your approach fits better. Just fetch the required elements and set the ids in addtlCountry()

You can just use clone and set its properties.

let countriesCounter = 1;

document.getElementById("add-line").addEventListener("click", addtlCountry);

function addtlCountry() {
    let addtlRow = document.getElementById('addtl_country_0');
    let table = document.getElementById('input_values');
    let clone = addtlRow.cloneNode(true);

    clone.id = "addtl_country_" + countriesCounter;
    const select = clone.querySelector("#countryToVisit");
    select.id = "countryToVisit_" + countriesCounter;
    // or change to "countryToVisit[" + countriesCounter + "]"
    select.name = "countryToVisit_" + countriesCounter;

    table.appendChild(clone);
    countriesCounter++;
}
<table id="input_values">
    <tr id="addtl_country_0">
        <td>
            <select name="countryToVisit" id="countryToVisit" required="">
                <option selected disabled value="select-country">Select Country</option>
                <option value="uk">United Kingdom</option>
                <option value="germany">Germany</option>
                <option value="chinahk">China/Hong Kong</option>
            </select>
        </td>
    </tr>
</table>
<button type="button" id="add-line">Add</button>

PS: You can change your ids from addtl_country_0 to addtl_country[0] names to use country[0]. This way you will get an array/list in your backend. The post name is the name, the indices of the list/array are in the brackets.This way you don't have to travel through the keys and organize them first.

Related