Vanilla Javascript:如何多次使用克隆的DOM节点?

时间:2018-09-09 09:41:24

标签: javascript html css disabled-input clonenode

我正在创建一个非常基本的购物车。

它具有相关的下拉菜单和一个按钮"Add more products",将在同一下拉菜单中再添加一行。

有2个下拉菜单第2个菜单必须保持disabled,直到在第1个菜单中选择了一个选项。输入的数量必须为disabled,直到在第二个菜单中选择了一个选项。 Add more products启用的数量为添加

我正在使用cloneNode()为新行添加代码。

由于它仅在我每次单击"Add more products"的{​​{1}}按钮时才创建克隆一次,因此有效

我正在使用最后添加的行来创建新的new_products();

添加了新行,但是问题是第二个菜单,并且此行中输入的数量已经为Clone

请尝试使用Vanilla(纯)JavaScript提供解决方案。

编辑1: 我来了一半。

在添加克隆之前,我尝试访问那些元素并更改enabled属性值。

disabled中:

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; 控制。

代码段:

input
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;
}

2 个答案:

答案 0 :(得分:2)

要在每次单击“添加更多产品” div.product时附加button.add_btn的克隆,您需要创建一个全局变量,该变量包含div.product的克隆会在页面加载时显示(我的意思是div.product的{​​{1}},select.second_selectinput.s_btn最初被禁用),然后是{{1 }},我们将创建一个新的button.add_btn元素,为它提供一个button.add_btn类,然后将div附加到product,然后使用{克隆元素的{1}}属性,并将其分配给新创建的div

一些评论和建议

我将在答案的末尾添加一个可运行的代码段,但在此之前,我想告诉您一些要点:

  • 您正在使用div#order_now,这不是明智的选择,您应该使用innerHTML方法。使用div方法,您可以同时附加多个事件(当然,当这些事件共享相同的处理逻辑时),也可以将任意数量的处理程序附加到同一事件(唯一的问题是客户端内存和性能问题)。 inline event listeners方法的另一个重要功能是final参数,该参数控制侦听器对冒泡事件的反应,使用内联事件时没有等效项。

      

    Learn more关于addEventListener方法

         

    Learn more关于addEventListener

  • 为使动态创建的元素不受事件(例如addEventListener元素上的'change'事件)的捕获,我们需要将事件附加到addEventListener上并检查应该执行哪个处理程序函数。换句话说,我们将使用events bubbling

      

    Learn more关于select,该Stackoverflow帖子可能会对您有所帮助。

  • 在我的回答中,您代码中的某些变量将不再用作document变量,而其他变量(希望您会注意到它们)。

  • 我从Event Delegqtion删除了所有多余的Event Delegation s属性,特别是在no_of_productsIDbutton s上元素,因为input在页面中必须唯一。另外,我删除了所有内联事件处理程序,因为我们将使用select方法。

  • 正如我所说,内联事件处理程序将由div.product方法替换,并且某些变量不再有用,要检查应执行哪个处理程序功能,我们将依靠{ {1}}的{​​{1}}属性,以查看哪个元素是当前事件的目标,因此我们将知道应该执行哪个处理程序函数。

      

    Learn more关于ID

  • 要允许对我们的代码进行更多控制(特别是处理函数),将使用addEventListener关键字引用当前事件的目标(像这样,不需要addEventListener变量,因为当每个元素是当前事件的目标时,将使用Event关键字来引用每个元素,即使动态创建的元素也将受到支持。即:在target函数中,Event.target表示单击的{ {1}}元素)。为此,我们将在处理程序函数上使用this方法,该方法允许我们指定处理程序函数中的no_of_products关键字所引用的元素。

      

    Learn more关于this方法。

话虽如此,下面是一个可运行的代码片段,用于说明:

new_products
this
button.add_btn

  

此Stackoverflow帖子可能会帮助您learn more了解callthis

希望我进一步推动了你。

答案 1 :(得分:1)

不幸的是,您在代码中必须犯很多错误。因此,我将仅描述重要的错误:

  1. 元素的id属性在完整的HTML页面中必须是唯一的。如果克隆某个元素并且它具有id属性,则必须创建一个新的id。而且因为您希望从按钮中删除id属性,所以我为您做了。
  2. 请勿使用element.disable禁用元素。大多数浏览器都支持它,但这不是标准。如果您看到ElementNode的文档,那么您将发现它们没有此属性。某些浏览器不支持。在这种情况下,请使用Element.setAttribute()Element.removeAttribute()函数。
  3. 添加下一个产品行的按钮必须不在产品行中。
  4. 如果不需要CSS属性float: left,请不要使用它。在您的情况下,您不需要它,因为您有inline-block个元素。我也更改了很多CSS代码。
  5. Do not reapeat your code again and again –这是非常糟糕的编程风格。阅读最后一个链接下的文章。因此,我缩短了您的代码–我编写了一些带有参数的函数。
  6. 通常,最好使用addEventListener方法而不是内联事件侦听器,但是在您的情况下,这是有争议的,因此,我不使用addEventListener –这样您就可以更好地理解我的代码。但是我在您的函数中添加了一个参数–请注意。

我编写了新代码,以便您没有任何类型的错误。享受吧!

完整的解决方案

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>