Search code examples
javascripthtmlcssdisabled-inputclonenode

Vanilla Javascript: How to use cloned DOM node multiple times?


I am creating a very basic shopping cart.

It has dependent drop down menus and a button "Add more products" which will add one more row of the same drop down menus.

There are 2 drop down menus 2nd menu must remain disabled until an option is selected in 1st menu. The quantity input must be disabled until an option is selected in 2nd menu. The Add more products is enabled quantity is add

I am using cloneNode() to add code for new row.

Since it works only once I am creating clone each time "Add more products" button is clicked which calls new_products();

I am using last added row for creating new Clone

The new row gets added but the problem is 2nd menu and quantity input in this row are already enabled.

Please try giving a solution in Vanilla(pure) JavaScript.

EDIT 1: I have came half way.

Before appending the clone I tried to access those elements and change the disabled attribute value.

In function new_products() :

var order = document.getElementById('order_now');
var product = document.getElementsByClassName('product');
var clone = product[no_of_products-1].cloneNode(true);
clone.getElementsByClassName('second_select')[0].disabled=true;
clone.getElementsByClassName('add_btn')[0].disabled=true;

But this only worked for 2nd drop down menu.

It's not working for quantity input control.

Code snippet:

var productsByCategory = {
  A: ["Select sub-product", "CNC 1", "CNC 2", "CNC 3", "CNC 4"],
  B: ["Select sub-product", "LASER 1", "LASER 2", "LASER 3", "LASER 4"],
  C: ["Select sub-product", "RUBBER 1", "RUBBER 2", "RUBBER 3", "RUBBER 4", "RUBBER 5"],
  D: ["Select sub-product", "PRECISION 1", "PRECISION 2", "PRECISION 3"]
}
var valuesByCategory = {
  A: ["", "A1", "A2", "A3", "A4"],
  B: ["", "B1", "B2", "B3", "B4"],
  C: ["", "C1", "C2", "C3", "C4", "C5"],
  D: ["", "D1", "D2", "D3"]
}

var no_of_products = 1;

function dropdown() {
  var select = document.getElementsByClassName('first_select');
  var selected = select[no_of_products - 1].value;
  var target = document.getElementsByClassName('second_select');
  var targetLength = target[no_of_products - 1].length
  /*console.log("Length"+target.length);*/
  for (var i = targetLength; i >= 0; i--) {
    /*console.log(i);*/
    target[no_of_products - 1].remove(i);
  }
  if (selected == 0) {
    var option = document.createElement("option");
    option.text = "Select Product first";
    option.value = "";
    target[no_of_products - 1].add(option);
    target[no_of_products - 1].disabled = true;
  }
  if (selected == 1) {

    for (var i in productsByCategory['A']) {
      var option = document.createElement("option"); //If this is outside the lopp then only last option gets included.
      option.text = productsByCategory['A'][i];
      option.value = valuesByCategory['A'][i];
      target[no_of_products - 1].add(option);
      target[no_of_products - 1].disabled = false;
    }

  } else if (selected == 2) {
    for (var i in productsByCategory['B']) {
      var option = document.createElement("option");
      option.text = productsByCategory['B'][i];
      option.value = valuesByCategory['B'][i];
      target[no_of_products - 1].add(option);
      target[no_of_products - 1].disabled = false;
    }
  } else if (selected == 3) {
    for (var i in productsByCategory['C']) {
      var option = document.createElement("option");
      option.text = productsByCategory['C'][i];
      option.value = valuesByCategory['C'][i];
      target[no_of_products - 1].add(option);
      target[no_of_products - 1].disabled = false;
    }
  } else {
    for (var i in productsByCategory['D']) {
      var option = document.createElement("option");
      option.text = productsByCategory['D'][i];
      option.value = valuesByCategory['D'][i];
      target[no_of_products - 1].add(option);
      target[no_of_products - 1].disabled = false;
    }
  }
}

function dropdown2() {
  var select = document.getElementsByClassName('second_select');
  var selected = select[no_of_products - 1].value;
  /*console.log(selected);*/
  var submit = document.getElementsByClassName('s_btn');
  submit[no_of_products - 1].disabled = false;
  var add = document.getElementById('add_button');
  add.disabled = false;
}

function new_products() {
  var order = document.getElementById('order_now');
  var product = document.getElementsByClassName('product');
  var clone = product[no_of_products - 1].cloneNode(true);
  clone.getElementsByClassName('second_select')[0].disabled = true;
  clone.getElementsByClassName('add_btn')[0].disabled = true;
  var add = document.getElementById('add_button');

  product[no_of_products - 1].removeChild(add);

  /*console.log(clone);*/

  order.appendChild(clone);

  no_of_products += 1;
}
body {
  height: 100vh;
  margin: 0px;
  overflow-y: auto;
  font-family: 'Roboto';
}

#clear {
  clear: both;
}

.content {
  display: flex;
  background-color: white;
  height: auto;
  margin-top: 0px;
  font-family: 'Roboto';
  z-index: -1;
  min-height: 88%;
}

.link-contents {
  position: relative;
  display: block;
  float: left;
  left: 0px;
  width: 100%;
}

.option-links {
  display: block;
  font-size: 30px;
  cursor: pointer;
}

#op1 {
  background-color: #cccccc;
}

select,
button,
input {
  position: relative;
  top: 5em;
  display: block;
  width: 12em;
  height: 2em;
}

button {
  width: 8em;
}

.first_select {
  position: relative;
  float: left;
  left: 10%;
}

.second_select {
  position: relative;
  float: left;
  left: 20%;
}

.s_btn {
  position: relative;
  float: left;
  left: 30%;
}

.add_btn {
  float: left;
  top: 6em;
  width: 10em;
  left: 5em;
}

.footer {
  display: block;
  max-height: 4%;
}

.option-contents {
  display: none;
}

#order_now {
  display: block;
}
<!DOCTYPE html>
<html>

<head>
  <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>
  <link rel="stylesheet" type="text/css" href="profile.css">
  <title></title>
</head>

<body>

  <div class="content">
    <div class="link-contents">
      <div class="option-contents" id="order_now">
        <div class="product">
          <select class="first_select" onchange="dropdown();">
            <option value="0">Select</option>
            <option value="1">CNS</option>
            <option value="2">Laser Cut</option>
            <option value="3">Rubber roller</option>
            <option value="4">Fixture</option>
          </select>

          <select class="second_select" onchange="dropdown2();" disabled>
            <option>Select Product first</option>
          </select>
          <input class="s_btn" type="number" min='1' value="1" disabled />
          <br/>
          <button class="add_btn" id="add_button" onclick="new_products();" disabled>Add more products</button>
          <div id="clear"></div>
        </div>
      </div>
    </div>

    <div id="clear"></div>

  </div>

  <div class="footer">
    A big thank you to all of you.
  </div>



</body>
<script type="text/javascript" src="profile.js"></script>

</html>


Solution

  • Unfortunately you have to much mistakes in your code. Because of this I will describe only important mistakes:

    1. The id attribute from an element must be unique in a full HTML page. If you clone some element and it has an id attribute then you have to create a new id. And because your wish is to remove id attribute from the button – so I did it for you.
    2. Do not use element.disable to disable an element. The most of browsers support it, but it is not standart. If you see the documentation for Element and for Node then you will see that they do not have this property. Some browser would not support it. Use for this case Element.setAttribute() and Element.removeAttribute() functions.
    3. The button for adding of next product row must be not in product row.
    4. Do not use CSS property float: left if you do not need it. In your case you do not need it because you have inline-block elements. I have changed a lot of your CSS code too.
    5. Do not reapeat your code again and again – it is very bad programming style. Read the article under the last link. Because of this I have shorted your code – I wrote some functions with parameters.
    6. Normaly it is better to use addEventListener method instead of inline event listeners, but in your case it is disputable and because of this I do not use addEventListener – so you can better understand my code. But I add one parameter in your functions – note it.

    I wrote the new code so that you do not have any type of errors. Enjoy it!

    Complete solution

    var productsByCategory =
    {
        A: ["Select sub-product", "CNC 1", "CNC 2", "CNC 3", "CNC 4"],
        B: ["Select sub-product", "LASER 1", "LASER 2", "LASER 3", "LASER 4"],
        C: ["Select sub-product", "RUBBER 1", "RUBBER 2", "RUBBER 3", "RUBBER 4", "RUBBER 5"],
        D: ["Select sub-product", "PRECISION 1", "PRECISION 2", "PRECISION 3"]
    };
    
    var valuesByCategory =
    {
        A: ["", "A1", "A2", "A3", "A4"],
        B: ["", "B1", "B2", "B3", "B4"],
        C: ["", "C1", "C2", "C3", "C4", "C5"],
        D: ["", "D1", "D2", "D3"]
    };
    
    //var no_of_products = 1; //WE DO NOT NEED IT
    
    function selectHelper(category, targetObj)
    {
        for(var i in productsByCategory[category])
        {
            var option = document.createElement("option");
    
            option.text = productsByCategory[category][i];
            option.value = valuesByCategory[category][i];
    
            targetObj.add(option);
        }
        
        setEnabled(targetObj);
    }
    
    function dropdown(obj)
    {
        var selected = obj.value,
            //second select:
            target = obj.nextElementSibling;
    
        for(var i = target.length; i--; )
            target.remove(i);
    
        if(selected == 0)
        {
            var option = document.createElement("option");
            option.text = "Select Product first";
            option.value = "";
    
            target.add(option);
            setDisabled(target);
            //set disabled input field:
            setDisabled(target.nextElementSibling)
        }
        else
        {
            if(selected == 1)
                selectHelper('A', target);
            else if(selected == 2)
                selectHelper('B', target);
            else if(selected == 3)
                selectHelper('C', target);
            else
                selectHelper('D', target)
        }
    }
    
    function dropdown2(obj)
    {
        setEnabled(obj.nextElementSibling);
    
        setEnabled(document.getElementsByClassName('add_btn')[0]);
    }
    
    function new_products()
    {
        var allProducts = document.getElementsByClassName('product');
            lastProduct = allProducts[allProducts.length - 1],
            clone = lastProduct.cloneNode(true);
    
        setDisabled(clone.getElementsByClassName('second_select')[0]);
        //set disabled input field:
        setDisabled(clone.getElementsByClassName('s_btn')[0]);
        setDisabled(document.getElementsByClassName('add_btn')[0]);
    
        //may be "insertBefore" is weird, but we do here insert new product after the last product:
        document.getElementById('order_now').insertBefore(clone, lastProduct.nextSibling);
    }
    
    function setDisabled(obj)
    {
        obj.setAttribute("disabled", "disabled");
    }
    
    function setEnabled(obj)
    {
        obj.removeAttribute("disabled");
    }
    body
    {
        height: 100vh;
        margin: 0px;
        overflow-y: auto;
        font-family: 'Roboto';
    }
    
    .content
    {
        background-color: white;
        height: auto;
        margin-top: 0px;
        z-index: -1;
        min-height: 88%;
    }
    
    .link-contents
    {
        position: relative;
        left: 0px;
        width: 100%;
    }
    
    .option-links
    {
        display: block;
        font-size: 30px;
        cursor: pointer;
    }
    
    #op1 {background-color: #cccccc}
    
    select, button, input
    {
        position: relative;
        top: 5em;
        width: 12em;
        height: 2em;
    }
    
    button {width: 8em}
    
    .first_select
    {
        position: relative;
        left: 10%;
    }
    
    .second_select
    {
        position: relative;
        left: 20%;
    }
    
    .s_btn
    {
        position: relative;
        left: 30%;
    }
    
    .add_btn
    {
        top: 6em;
        width: 10em;
    }
    
    .footer
    {
        display: block;
        max-height: 4%;
    }
    
    .option-contents {display: none}
    #order_now {display: block}
    <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>
    <link rel="stylesheet" type="text/css" href="profile.css">
    
    <div class="content">
        <div class="link-contents">
            <div class="option-contents" id="order_now">
                <div class="product">
                    <select class="first_select" onchange="dropdown(this)">
                        <option value="0">Select</option>
                        <option value="1">CNS</option>
                        <option value="2">Laser Cut</option>
                        <option value="3">Rubber roller</option>
                        <option value="4">Fixture</option>
                    </select>
    
                    <select class="second_select" onchange="dropdown2(this)" disabled>
                        <option>Select Product first</option>
                    </select>
    
                    <input class="s_btn" type="number" min='1' value="1" disabled />
                </div>
                <center><button class="add_btn" onclick="new_products()" disabled>Add more products</button></center>
            </div>
        </div>
    </div>
    <div class="footer">A big thank you to all of you.</div>