动态插入新行后增加和更新总数中的值

Increment and update value in the total number after insert new rows dynamically

编辑:我已经用答案更新了代码。

我有一个运行良好的增量函数。然而:

1.我想根据其中一个跨度中可用的总数设置一些限制。比如10。所以递增不能超过10。#DONE

  1. 另一个问题是我计划有多行,在保存之前我想确保如果我们计算每一行的增量,它也不应该超过 10。如果它动态地减少总数(跨度)会很好。

我正在使用“添加”按钮动态添加行,如何才能添加实际使用当前功能的新闻行?我的行只是克隆第一行并且禁用了增量功能。

document.addEventListener('DOMContentLoaded', async function() {
  document.querySelector('#addlocationdest').addEventListener('click', add);
});
function add() {
    var x = 1;
    var container = document.getElementById('destination');
    var detail = document.getElementById('row');
    var clone = detail.cloneNode(true);
    clone.id = "destination" + x;
    x++;
    container.appendChild(clone);

}

window.addEventListener("load", () => {
  let elTotalQuantity = document.querySelector("#totalqty");
  let totalQuantity = parseInt(elTotalQuantity.innerHTML);
  
  function getSumOfRows() {
    let sum = 0;
    for (let input of document.querySelectorAll("form .row > input.quantity"))
      sum += parseInt(input.value);
    return sum;
  }
  
  for (let row of document.querySelectorAll("form .row")) {
    let input = row.querySelector("input");
    row.querySelector(".increment").addEventListener("click", () => {
      if (getSumOfRows() >= totalQuantity) return;
      input.value++;
      elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
    });
    row.querySelector(".decrement").addEventListener("click", () => {
      if (input.value <= 0) return;
      input.value--;
      elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
    });
  }
});
<div id="location" class="hide">
                    <div class="title">Transfer details</div><br>
                    <div class="line padded-s">Total Quantity: <span>10</span></div>
                    <br>
                        <form>
                            <label>New Total Quantity at this location: <span id="totalqty">10</span></label>
                            <br>
                            <div id="destination">
                                <div id="row" class="row">
                                    <button type="button" class="decrement">-</button>
                                    <input type="text" class="quantity" value="0" readonly/>
                                    <button type="button" class="increment">+</button>
                                    <a>Location: </a>
                                    <input type="text" class="location" value="0" readonly/>
                                </div>
                            </div>
                        </form>

                        <label>Total being transfer: <p id="total-sum"></p></label>
                        <br>
                        <button type="button" id="addlocationdest">ADD</button>
            <button type="button" id="removelocationdest">REMOVE</button>
                </div>

QuerySelector 仅 select 第一次出现,因此您还没有真正向第二个“行”添加侦听器。您应该使用 querySelectorAll,但不要使用唯一 ID,而是使用 类.

<input class="increment" type="button" value="+" />
  1. 现在您可以使用document.querySelectorAll(".increment")获取数组中的所有元素。

  2. 您可以使用parentElement在DOM中遍历。通过知道您单击了哪个按钮,您可以遍历到表单元素,然后 select 第一个子元素 - 这是一个输入。一种更动态的方法是使用 querySelector 到 select 输入,以防将来 HTML 发生变化。无论如何,这就是你如何根据按钮在 DOM.

    中的位置知道要操作哪个输入
  3. 我添加了两个全局变量,totalSummaxSummaxSum 是从您的 span 元素(我为其分配了一个唯一 ID)中获取的。 totalSum 确保所有输入的总和不超过 maxSum.

  4. 您有一些重复的代码,所以我将其重构为一个新方法:changeValue.

总而言之,我认为代码不言自明。

哦,这段代码没有考虑到用户可以更改输入中的值。我会把它留给你在每个文本输入上使用“oninput”侦听器来解决。

var totalSum = 0;  // 3
var maxSum = 0
var totalSumElement = null;

document.addEventListener('DOMContentLoaded', async function() {
  totalSumElement = document.getElementById('total-sum');
  maxSum = document.getElementById('max-sum').innerText;

  var incrementElements = document.querySelectorAll('.increment');  // 1
  var decrementElements = document.querySelectorAll('.decrement');

  addListener('click', incrementElements, incrementValue);
  addListener('click', decrementElements, decrementValue);
});


function addListener(type, elementArr, func) {
  for (element of elementArr) {
    element.addEventListener(type, func);
  }
}

function withinRange(newValue) {
  var maxReached  = newValue > maxSum;  // 3
  var zeroReached = newValue < 0;

  return !maxReached && !zeroReached;
}

function changeValue(event, change) {  // 4
  if (withinRange(totalSum + change)) {
    let parent = event.currentTarget.parentElement;  // 2
    let input  = parent.children[0];
    let value  = parseInt(input.value) || 0;
    
    if (withinRange(value + change)) {
      input.value = value + change;
      totalSum = totalSum + change;
    }
  }

  totalSumElement.textContent = `Total: ${totalSum}`;
}

function incrementValue(event) {
  changeValue(event, 1);
}

function decrementValue(event) {
  changeValue(event, -1);
}
#totalqty {
  padding-bottom: 1rem;
}
<div id="totalqty" class="line padded-s">Total Quantity: <span id="max-sum">10</span></div>

<form>
  <input type="text" value="0" />
  <input class="increment" type="button" value="+" />
  <input class="decrement" type="button" value="-" />
</form>
<form>
  <input type="text" value="0" />
  <input class="increment" type="button" value="+" />
  <input class="decrement" type="button" value="-" />
</form>

<p id="total-sum"></p>

序言
只要总量固定在 script-execution 的开头,就可以了。否则,最好将实际允许的总量保存为一个属性,并使用 MutationObserver. That way you can update your max. value in your code dynamically, when the total quantity-attribute changes. You can define custom attributes 观察它,将它们命名为“data-*”,其中“*”是自定义名称。

您的问题的解决方案
您在多个元素上使用相同的 ID。你的意思是类,所以把id="increment"改成class="increment"decrement也是一样。

因为我们不想用按钮输入东西,而是想给它们添加监听器,所以我认为实际使用 <button> 会更好。在表单中,按钮充当type="submit",这是我们不想要的,因此我们需要将其更改为type="button"

由于行数和总数实际上是属于一起的,所以将它们一起放在一个 <form>-元素中更为明智。但是,您仍然可以使用 <div>.

将按钮和输入分组为一行

现在关于行值的 in-/decrementing 和总数量:

  1. 将允许的总量保存在一个变量中
  2. 将event-listener添加到相应的按钮
  3. 如果操作有效,更改行的值
  4. 更新总数量为totalQuantity - getSumOfRows()

为了动态添加新行,我们创建并设置了这样一个元素,并将其附加到表单中。请参阅下面的 appendNewRow()-函数。

旁注
我已将 readonly 属性添加到 input-fields 以便您无法通过键盘输入数字。

window.addEventListener("load", () => {
  let elTotalQuantity = document.querySelector("#totalqty");
  let totalQuantity = parseInt(elTotalQuantity.innerHTML);
  
  function getSumOfRows() {
    let sum = 0;
    for (let input of document.querySelectorAll("form .row > input.quantity"))
      sum += parseInt(input.value);
    return sum;
  }
  function updateTotalQuantity() {
      elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
  }
  
  function appendNewRow() {
    let row = document.createElement("div");
    row.classList.add("row");
    let child;
    
    // input.quantity
    let input = document.createElement("input");
    input.classList.add("quantity");
    input.value = "0";
    input.setAttribute("readonly", "");
    input.setAttribute("type", "text");
    row.append(input);
    
    // button.increment
    child = document.createElement("button");
    child.classList.add("increment");
    child.innerHTML = "+";
    child.setAttribute("type", "button");
    child.addEventListener("click", () => {
      if (getSumOfRows() >= totalQuantity) return;
      input.value++;
      updateTotalQuantity();
    });
    row.append(child);
    
    // button.increment
    child = document.createElement("button");
    child.classList.add("decrement");
    child.innerHTML = "-";
    child.setAttribute("type", "button");
    child.addEventListener("click", () => {
      if (input.value <= 0) return;
      input.value--;
      updateTotalQuantity();
    });
    row.append(child);
    
    // button.remove-row
    child = document.createElement("button");
    child.classList.add("remove-row");
    child.innerHTML = "Remove";
    child.setAttribute("type", "button");
    child.addEventListener("click", () => {
      row.remove();
      updateTotalQuantity();
    });
    row.append(child);
    
    document.querySelector("form .rows").append(row);
  }
  
  document.querySelector("form .add-row").addEventListener("click", () => appendNewRow());
  
  appendNewRow();
});
<form>
  <label>Total Quantity: <span id="totalqty">10</span></label>
  <br>
  <div class="rows">
  </div>
  <button type="button" class="add-row">Add new row</button>
</form>