Увеличение и обновление значения в общем числе после динамической вставки новых строк

РЕДАКТИРОВАТЬ: я обновил код с ответами.

У меня есть функция увеличения, которая работает нормально. Однако:

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>


person Gabriel    schedule 06.11.2020    source источник
comment
Будет ли число для общего количества динамическим? И какое число следует уменьшить, если оно выходит за пределы общего количества после отправки формы? Разве вы не можете НЕ увеличивать количество строк, когда их увеличение приведет к тому, что фактическое общее количество переполнит заданное общее количество?   -  person Oskar Grosser    schedule 06.11.2020
comment
да, общее количество будет динамическим, общее количество будет уменьшено, а остальные будут распределены по определенным строкам в базе данных, но пока это не важно. Когда я увеличиваю число в строке, это уменьшает общее число. Общее число для справки. Думайте об этом как о текущем количестве в этом конкретном месте.   -  person Gabriel    schedule 06.11.2020
comment
Таким образом, общее число — это не общее количество чисел ниже, а общее количество, которое еще можно распределить на числа ниже?   -  person Oskar Grosser    schedule 06.11.2020
comment
вот именно этот номер будет раздаваться и его нужно соответственно обновлять   -  person Gabriel    schedule 06.11.2020
comment
Должна ли быть возможность ввести число в поле ввода с клавиатуры или изменить значение можно только с помощью кнопок? Разрешение изменения с помощью клавиатуры сделало бы это немного сложнее, но все же возможно.   -  person Oskar Grosser    schedule 06.11.2020
comment
Мне действительно не нужно писать номер, просто кнопка в порядке   -  person Gabriel    schedule 06.11.2020


Ответы (2)


Пролог
Пока общее количество фиксируется в начале выполнения скрипта, это работает. В противном случае было бы лучше сохранить фактическое разрешенное общее количество в качестве атрибута и наблюдать за ним с помощью ссылки Обозреватель мутаций. Таким образом, вы можете обновить свой макс. значение в вашем коде динамически, когда общий атрибут количества изменяется. Вы можете определить пользовательские атрибуты, назвав их data- * где * — пользовательское имя.

Решение вашей проблемы
Вы используете один и тот же идентификатор для нескольких элементов. Вы имели в виду классы, поэтому измените id="increment" на class="increment" и то же самое на decrement.

Поскольку мы не хотим что-то вводить с помощью кнопок, а добавляем к ним слушателя, я бы сказал, что лучше использовать <button>. В формах кнопки действуют как type="submit", что нам не нужно, поэтому нам нужно изменить его на type="button".

Поскольку строки и общее количество на самом деле связаны друг с другом, разумнее поместить их вместе в один <form>-элемент. Однако вы по-прежнему можете сгруппировать кнопки и входы в виде строки вместе, используя <div>.

Теперь относительно увеличения/уменьшения значений строки и общего количества:

  1. Сохранить разрешенное общее количество в переменной
  2. Добавить прослушиватель событий к соответствующим кнопкам
  3. Если действие допустимо, измените значение строки
  4. Обновить общее количество до totalQuantity - getSumOfRows()

Чтобы динамически добавлять новые строки, мы создаем и настраиваем такой элемент и добавляем его в форму. См. функцию appendNewRow() ниже.

Примечание
Я добавил атрибут readonly в поля ввода, чтобы вы не могли вводить числа с клавиатуры.

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>

person Oskar Grosser    schedule 06.11.2020
comment
Если мы добавим второй вход в строку, общее количество вернет NaN после первого приращения. - person Gabriel; 06.11.2020
comment
Вы можете дать вводу, содержащему количество, класс, например quantity, и запросить его. Таким образом, это должно работать. Обновлен ответ, чтобы включить то, что я только что сказал. - person Oskar Grosser; 06.11.2020
comment
Как я могу отправить значения в строках в базу данных? Я думаю использовать что-то вроде SELECT * FROM mytable, где itemID=?? & location=??.. если возвращает 1 -> ОБНОВИТЬ значение, а если возвращает 0 -> ВСТАВИТЬ и создать новую строку - person Gabriel; 09.11.2020
comment
Похоже, вы используете SQL-базу данных. Ваша идея кажется достаточно хорошей для того, чего я думаю, вы пытаетесь достичь. Но для лучшего ответа вы должны открыть новый вопрос. - person Oskar Grosser; 09.11.2020
comment
Да, действительно, я использую MySQL Большое спасибо, если вы хотите взглянуть на вопрос, вот ссылка stackoverflow.com/questions/64761304/ - person Gabriel; 10.11.2020

QuerySelector выбирает только первое вхождение, поэтому вы на самом деле не добавили прослушиватель во вторую строку. Вы должны использовать querySelectorAll, но вместо уникальных идентификаторов используйте классы.

<input class="increment" type="button" value="+" />
  1. Теперь вы можете использовать document.querySelectorAll(".increment") для получения всех элементов массива.

  2. Вы можете перемещаться по DOM, используя parentElement. Зная, какую кнопку вы нажали, вы можете перейти к элементу формы, а затем выбрать первый дочерний элемент, который является входом. Более динамичным способом было бы использование querySelector для выбора ввода на случай изменения HTML в будущем. Во всяком случае, именно так вы можете узнать, каким вводом манипулировать, основываясь на том, где кнопки находятся в DOM.

  3. Я добавил две глобальные переменные, totalSum и maxSum. maxSum извлекается из вашего элемента span (которому я присвоил уникальный идентификатор). 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>

person Rickard Elimää    schedule 06.11.2020
comment
У меня уже есть функция addListener, и если я переименую вашу, я получу ошибку ReferenceError: элемент не определен - person Gabriel; 06.11.2020
comment
Вам нужно отладить, если это так. Добавьте debugger; к переименованному методу addListener и запустите страницу с открытой в браузере консолью веб-разработчика. По какой-то причине elementArr пусто. Вы также можете console.log() incrementElementsи decrementElements проверить, не пусты ли массивы. - person Rickard Elimää; 06.11.2020