получение значений из формы до и после их изменения, js
в интерент-магазине есть + и - для добавления товара в корзину и между ними есть input в который вручную можно ввести данные, не прибегая к + или -. У товара есть пакетирование и вручную ввести можно только согласно нему. Так, к примеру, если пакетирование = 12, а юзер ввёл "20", то ему исправит на 24.
Есть некое недопонимание в том как JS извлекает данные из формы. Недопонимание связано с тем, то я могу понять истинное значение переменной. Если я вывожу её в console.log(), то получаю значение ДО редактирования инпута, но там же в инструменте Watch я получаю значение ПОСЛЕ редактирования. Прикреплю скриншот и куски кода, чтоб стало понятно.
Скриншот, на котором видна суть вопроса: 
И собственно код: 1. catalog-component.js содержит логику определения количества товара, если оно указано не кратно пакетированию.
var CatalogComponent = {
productPage: false,
count: 0,
init: function() {
var self = this;
var $products = $('body');
$products.on('keyup', '.handle-quantity', function() {
self.setCount($(this));
});
},
changeCount: function($input, plus) {
var count = parseInt($input.val());
var maxCount = $input.parent().find('div.hidden').data('available')?$input.parent().find('div.hidden').data('available'):9999999;
var minCount = $input.parent().find('div.hidden').data('min_quantity');
var stepCount = $input.parent().find('div.hidden').data('step_quantity');
if (plus) {
if ((count + stepCount ) <= maxCount) {
count+=stepCount;
}else{
count = maxCount;
}
} else {
if(count == maxCount){
if((count - minCount) % stepCount != 0 ){
count -= minCount;
if(count<stepCount){
count = stepCount;
}
count = Math.ceil(count/stepCount)*stepCount;
if((count+minCount)<minCount){
count = minCount;
}else{
if((count+minCount)>maxCount){
count = maxCount-minCount;
count = Math.floor(count/stepCount)*stepCount;
}
count +=minCount;
}
}
if(count == maxCount){
count-=stepCount;
}
}else{
if ((count - stepCount) >= minCount) {
count-=stepCount;
}else{
count = minCount;
}
}
}
this.count = parseInt(count);
this.setCount($input, count);
},
setCount: function($input, count) {
var maxCount = $input.parent().find('div.hidden').data('available')?$input.parent().find('div.hidden').data('available'): ($input.parent().find('div.hidden').data('min_quantity')) * 10000;
var minCount = $input.parent().find('div.hidden').data('min_quantity');
var stepCount = $input.parent().find('div.hidden').data('step_quantity');
if (count === undefined) {
count = $input.val();
}
if (count === '') {
return ;
}
if (!$.isNumeric(count)) {
count = minCount;
}
if (count > maxCount) {
count = maxCount;
} else if (count === 0) {
count = minCount;
}
count = parseInt(count);
if((count - minCount) % stepCount != 0 && count != maxCount){
count -= minCount;
if(count<stepCount){
count = stepCount;
}
count = Math.ceil(count/stepCount)*stepCount;
if((count+minCount)<minCount){
count = minCount;
}else{
if((count+minCount)>maxCount){
count = maxCount-minCount;
count = Math.floor(count/stepCount)*stepCount;
}
count +=minCount;
}
}
$input.val(parseInt(count));
},
};
Тут после того, как логика отработала я запихиваю окончательное значение в свойство объекта this.count = parseInt(count);
Потом, следующим кодом я достаю это свойство, надеясь его использовать
function getCnt(){
return CatalogComponent.count;
}
// изменение сумм при ручном изменении кол-ва
$(".handle-quantity").on('input', function(){
let parent = $(this).closest(".t812__pricelist-item");
let id = parent.attr('id');
let count = $(this).val();
let price = parent.find(".prod_price").text();
let prodSum = Math.abs((count * price).toFixed(2));
console.log(getCnt());
// изменение суммы по товару
parent.find(".sum").text(prodSum);
// изменение суммы корзины
changeTotalSum($(this));
//изменение кол-ва товара в базе
var arr = {'id': id, 'count': count};
changeSumInBd(arr);
});
Но в нём лежит значение инпута ДО его изменения. Это и продемонстрировано на скриншоте. Соответственно мой вопрос в там, как передать/получить значение ПОСЛЕ изменения инпута.
Ибо получается так, что вставить полученное значение в инпут можно: $input.val(parseInt(count));, а использовать его для вычислений нельзя, т.к. получается старое значение.
---Пока писал вопрос, подумал, что у меня в 2 местах инициализируется реакция на изменения инпута. Может быть в этом вся соль.
Ответы (2 шт):
Если идти по порядку разбора Вашего кода:
1. Нигде не указаны вызовы следующих функций
a) changeCount
b) changeTotalSum($(this));(ну тут понятно почему)
с) changeSumInBd(arr); (Тут тоже)
По-этому пока временно убираем эти функции из кода. Вы их добавите в дальнейшем самостоятельно.
2. В строке
$products.on('keyup', '.handle-quantity', function() {
self.setCount($(this));
});
Вы вызываете метод keyup, что будем менять значение в тот момент, когда клавиша отжата.
Соответственно, если будут вводить двузначные и так далее цифры, то данные будут меняться, не давая до конца ввести свои данные.
В данном случае лучше использовать метод потери фокуса поля - blur.
3. В функция setCount ожидает от Вас передачи 2 параметров function($input, count).
Вы же передаёте в него один, используя вызов функции self.setCount($(this));
Понятно, что в дальнейшем Вы проверяете данный параметр на undefined - if (count === undefined). Но в этом нет смысла, так как он всегда будет undefined, так как данные туда не передаются и переменная не имеет объявленного значения.
Либо передавайте туда значение в функции используя self.setCount($(this), $(this).val()); либо по аналогии с функцией changeCount получайте этот параметр уже в самой функции.
var count = parseInt($input.val());
4 Вы используете изменение поля ввода в самой функции setCount добавляя $input.val(parseInt(count));
Так же прям там вы получаете необходимые Вам данные для расчёта, этом случае Вам необходимо тогда подкорректировать $(".handle-quantity").on('input', function(){
(Там Вы получаете данные, которые могут отличаться от тех, которые Вы корректируете при вводе)
К примеру, Вам необходимо получать count не через $(this).val();, а через вызов самой функции setCount добавив в конец этой функции после вставки значения в поле return count;. Чтобы данные у Вас были одинаковые и по цене и по количеству.(Пример получения будет чуть ниже)
5 Вы сделали временную функцию для вызова getCnt, которая должна возвращать Вам count, но в данном случае у Вас не вызываются функции внутри, по-этому Вы всегда будете получать 0.
Изначально в функции getCnt нет смысла, так как Вы можете вызывать функции прямо из CatalogComponent.
В этом случае Вы уже получите значение, которое возвращаете в return:
CatalogComponent.init()
Соответственно, для получения значения в count в $(".handle-quantity").on('input', function(){
let count = CatalogComponent.init();
Так же Вам надо подкорректировать расчёты самой суммы, учитывая шаг товара. (Сейчас он работает не совсем корректно, менять не стал, мало ли чего-то не знаю в Вашем html).
Он всегда делает обработку в большую сторону
Но в целом должно выглядеть примерно так:
var CatalogComponent = {
productPage: false,
count: 0,
init: function() {
var self = this;
var $products = $('body');
$products.on('blur', '.handle-quantity', function() {
self.setCount($(this), $(this).val());
});
},
setCount: function($input, count) {
var maxCount = $input.parent().find('div.hidden').data('available') ? $input.parent().find('div.hidden').data('available') : ($input.parent().find('div.hidden').data('min_quantity')) * 10000;
var minCount = $input.parent().find('div.hidden').data('min_quantity');
var stepCount = $input.parent().find('div.hidden').data('step_quantity');
if (count === undefined) {
count = $input.val();
}
if (count === '') {
return;
}
if (!$.isNumeric(count)) {
count = minCount;
}
if (count > maxCount) {
count = maxCount;
} else if (count === 0) {
count = minCount;
}
count = parseInt(count);
if ((count - minCount) % stepCount != 0 && count != maxCount) {
count -= minCount;
if (count < stepCount) {
count = stepCount;
}
count = Math.ceil(count / stepCount) * stepCount;
if ((count + minCount) < minCount) {
count = minCount;
} else {
if ((count + minCount) > maxCount) {
count = maxCount - minCount;
count = Math.floor(count / stepCount) * stepCount;
}
count += minCount;
}
}
$input.val(parseInt(count));
return count;
}
};
// изменение сумм при ручном изменении кол-ва
$(".handle-quantity").on('input', function(){
let parent = $(this).closest(".t812__pricelist-item");
let id = parent.attr('id');
let count = CatalogComponent.init();
let price = parent.find(".prod_price").text();
let prodSum = Math.abs((count * price).toFixed(2));
/* // изменение суммы по товару
parent.find(".sum").text(prodSum);
// изменение суммы корзины
changeTotalSum($(this));
//изменение кол-ва товара в базе
var arr = {'id': id, 'count': count};
changeSumInBd(arr);*/
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class = "tbcs">
<div class="order__minus order__minus--large" tabindex="2" style="font-weight:100; margin: 0;">
<span>-</span>
</div>
<div class="order__count order__count--large">
<span>
<input id="product_1" type="number" value="15" class="handle-quantity" style="width: 50px; text-align: center; outline: none; border: none; font-weight:100; font-size: 14px;">
<div class="hidden js_qant"
data-available='40'
data-min_quantity='10'
data-step_quantity='10'
></div>
</span>
</div>
<div class="order__plus order__plus--large" tabindex="1" style="font-weight:100; margin: 0;">
<span>+</span>
</div>
</div>
Denis640Kb, огромнейшее спасибо за ваш ответ, дельные замечания, суть понял, технически реализовал. выкладываю ниже, может кому-то пригодится. Основная суть ошибки заключалась в том, что инициализация событий была в двух местах и каждому обработчику этих событий доставались то одинаковые, то разные данные + конечно же все остальные замечания. Собрал всё в кучу, проработал по списку все замечания.
var CatalogComponent = {
productPage: false,
url: document.location.pathname,
basket: 'basket',
page: 'page',
count: 0,
init: function () {
var $products = $('body');
self = this;
//устанавливаем начальное значение суммы и количества товара в корзине при открытии попапа с товаром в карточке товара
$products.on('click', '.t754__content', function () {
var str = $(this).find(".js-product-name").attr("field");
var id = str.replace("li_title__", "");
var parent = $products.find("#t754__product-" + id);
var price = parent.find("div.js-product-price").text();
var quant = parent.find(".handle-quantity").val();
var summ = (price * quant).toFixed(2);
var product_id = parent.find(".js-product-name").data('product-id');
$('.tt_summ').html(summ).val();
//вычисляем сколько уже в корзине этого товара
inCartYet(product_id);
});
//реакция на нажатие + (и в корзине и в карточке)
$products.on('click', '.order__plus', function () {
//изменяется кол-во в инпуте
self.changeCount($(this).parent().find('.handle-quantity'), true);
if (getUrl() == CatalogComponent.page) {
//изменяется сумма на странице
changeSumInPage(this)
}
if (getUrl() == CatalogComponent.basket) {
//изменяется сумма в корзине
changeSumInBasket($(this));
}
});
//реакция на нажатие - (и в корзине и в карточке)
$products.on('click', '.order__minus', function () {
//изменяется кол-во в инпуте
self.changeCount($(this).parent().find('.handle-quantity'), false);
if (getUrl() == CatalogComponent.page) {
//изменяется сумма на странице
changeSumInPage(this)
}
if (getUrl() == CatalogComponent.basket) {
//изменяется сумма в корзине
changeSumInBasket($(this));
}
});
//изменения при ручном вводе
$products.on('change', '.handle-quantity', function () {
self.setCount($(this), $(this).val());
if (getUrl() == CatalogComponent.page) {
changeSumInPage(this, self.count);
}
if (getUrl() == CatalogComponent.basket) {
let parent = $(this).closest(".t812__pricelist-item");
let id = parent.attr('id');
let count = self.count;
let price = parent.find(".prod_price").text();
let prodSum = Math.abs((count * price).toFixed(2));
// изменение суммы по товару
parent.find(".sum").text(prodSum);
// изменение суммы корзины
changeTotalSum($(this));
//изменение кол-ва товара в базе
var arr = {'id': id, 'count': count};
changeSumInBd(arr);
}
});
//добавление товара в корзину из карточки товара
$products.on('click', '.order__basket', function () {
var order = $(this).closest('.order'),
productCount = order.find('.handle-quantity').val(),
productId = order.data('product-id');
if (productCount === undefined)
productCount = order.find('.order__count span')[0].textContent;
if (self.productPage) {
productId = $('.product-card__title').data('product-id');
}
$.ajax({
url: '/atb',
method: "POST",
data: {'product_id': productId, 'count': productCount},
headers: {'X-CSRF-TOKEN': getToken()},
success: function (data) {
if (data === 'Added to cart') {
var countCart = $('.header__basket-note')[0].textContent;
$('.header__basket-note')[0].textContent = +countCart + 1;
swal({
text: "В корзину добавлено " + productCount + " шт.",
className: "my_swal_ok",
timer: 800,
});
setTimeout(inCartYet, 800, productId);
} else if (data === 'Add count product') {
swal({
text: "добавлено ещё " + productCount + " шт.",
className: "my_swal_ok",
timer: 600,
});
setTimeout(inCartYet, 600, productId);
} else if (data === 'no relations') {
swal({
title: "",
text: 'Вы не можете добавлять в корзину товар этого Поставщика. Сначала вам надо заключить соглашение с ним',
icon: "success",
className: "my_swal_error",
timer: 2000,
position: "bottom-start",
timerProgressBar: true,
});
}
}
});
});
if ($('#product-page').length > 0) {
this.productPage = true;
}
},
changeCount: function ($input, plus) {
var count = parseInt($input.val());
var maxCount = $input.parent().find('div.hidden').data('available') ? $input.parent().find('div.hidden').data('available') : ($input.parent().find('div.hidden').data('min_quantity')) * 10000;
var minCount = $input.parent().find('div.hidden').data('min_quantity');
var stepCount = $input.parent().find('div.hidden').data('step_quantity');
if (plus) {
if ((count + stepCount) <= maxCount) {
count += stepCount;
} else {
count = maxCount;
}
} else {
if (count == maxCount) {
if ((count - minCount) % stepCount != 0) {
count -= minCount;
if (count < stepCount) {
count = stepCount;
}
count = Math.ceil(count / stepCount) * stepCount;
if ((count + minCount) < minCount) {
count = minCount;
} else {
if ((count + minCount) > maxCount) {
count = maxCount - minCount;
count = Math.floor(count / stepCount) * stepCount;
}
count += minCount;
}
}
if (count == maxCount) {
count -= stepCount;
}
} else {
if ((count - stepCount) >= minCount) {
count -= stepCount;
} else {
count = minCount;
}
}
}
this.setCount($input, count);
},
setCount: function ($input, count) {
var maxCount = $input.parent().find('div.hidden').data('available') ? $input.parent().find('div.hidden').data('available') : ($input.parent().find('div.hidden').data('min_quantity')) * 10000;
var minCount = $input.parent().find('div.hidden').data('min_quantity');
var stepCount = $input.parent().find('div.hidden').data('step_quantity');
if (count === undefined) {
count = $input.val();
}
if (count === '') {
count = minCount;
}
if (!$.isNumeric(count)) {
count = minCount;
}
if (count > maxCount) {
count = maxCount;
} else if (count === 0) {
count = minCount;
} else if (count <= minCount) {
count = minCount
}
count = parseInt(count);
if ((count - minCount) % stepCount != 0 && count != maxCount) {
count -= minCount;
if (count < stepCount) {
count = stepCount;
}
count = Math.ceil(count / stepCount) * stepCount;
if ((count + minCount) < minCount) {
count = minCount;
} else {
if ((count + minCount) > maxCount) {
count = maxCount - minCount;
count = Math.floor(count / stepCount) * stepCount;
}
count += minCount;
}
}
this.count = parseInt(count);
$input.val(parseInt(count));
},
};
// изменение сумм при кликах на + и - в карточке товара
function changeSumInPage(data, count = null) {
var parent = $(data).closest(".t754__product-full");
var price = parent.find("div.js-product-price").text();
if (count) {
var summ = (count * price).toFixed(2);
} else {
var quant = parent.find(".handle-quantity").val();
var summ = (price * quant).toFixed(2);
}
$('.tt_summ').html(summ).val();
}
// изменение сумм при кликах на + и - в корзине
function changeSumInBasket(data) {
let parent = $(data).closest(".t812__pricelist-item");
let id = parent.attr('id');
// let znak = Number( $(data).children().eq(0).text() + "1" );
// let step = parent.find('.hidden.js_qant').data('step_quantity'); // Получить шаг
let min = parent.find('.hidden.js_qant').data('min_quantity'); // Получить минимальное значение
let count = +$("#product_" + id).val();
// count += znak * step;
if (count <= 0 || !count) {
count = min;
}
let price = parent.find(".prod_price").text();
let prodSum = Math.abs((count * price).toFixed(2));
// изменение суммы товара в корзине
parent.find(".sum").text(prodSum);
// изменение суммы корзины
changeTotalSum(data);
//изменение кол-ва товара из корзины в базе
arr = {'id': id, 'count': count};
changeSumInBd(arr);
}
//изменение кол-ва товара в базе
function changeSumInBd(arr) {
$.ajax({
url: '/ccp',
method: 'POST',
data: arr,
headers: {'X-CSRF-TOKEN': getToken()},
});
}
//изменение суммы корзины при кликах на + и -
function changeTotalSum(data, del = null) {
let parent = $(data).closest(".cart_js");
let id = parent.attr('id').replace("cart_", '');
let prods = parent.children('.t812__pricelist-item');
let totalSum = 0;
prods.each(function () {
if ($(this).css('display') != 'none') {
totalSum += +($(this).find(".sum").text());
}
});
if (del > 0) {
totalSum = totalSum - del;
}
$("#totalsumm_" + id).text((totalSum).toFixed(2));
// изменение суммы до мин заказа + скрытие блока
changeRest(data);
}
//изменение суммы, оставшейся до минимального заказа в корзине + скрытие блока
function changeRest(data) {
var rest = getRest(data);
var val_rest = (getMinOrder(data) - getTotalSum(data)).toFixed(2);
var id = getId(data);
if (val_rest <= 0) {
val_rest = 0;
$("#fin_help_" + id).animate({height: 'hide'}, 500);
} else {
$("#fin_help_" + id).animate({height: 'show'}, 500);
}
rest.text(val_rest);
}
function getMinOrder(data) {
var id = getId(data);
var min_order = $("#min_order_" + id).text();
return parseFloat(min_order);
}
//получение контекста, в котором хранится сумма, оставшаяся до минимального заказа
function getRest(data) {
let id = getId(data);
let rest = $("#rest_" + id);
return rest;
}
//получение общей суммы из корзины
function getTotalSum(data) {
let id = getId(data);
let totalSum = $("#totalsumm_" + id).text();
return parseFloat(totalSum);
}
//получение id заказа в корзине внутри которого был инициирован this
function getId(data) {
let parent = $(data).closest(".cart_js");
let id = parent.attr('id').replace("cart_", '');
return id;
}
//получение csfr-токена
function getToken() {
return $('meta[name="csrf-token"]').attr('content');
}
// проверяем, вызывается событие из корзины или из карточки товара
function getUrl() {
if ((CatalogComponent.url).indexOf('basket') >= 0) {
return CatalogComponent.basket;
} else {
return CatalogComponent.page;
}
}
function inCartYet(product_id) {
var price = $('#price_' + product_id).attr('content');
$.ajax({
url: '/cib',
method: "POST",
cache: false,
data: {'product_id': product_id},
headers: {'X-CSRF-TOKEN': getToken()},
success: function (countInCart) {
if (countInCart > 0) {
$('#js-text_in_cart_' + product_id).html("В <a href=\"/basket\">корзине</a> " + countInCart + ' шт. на сумму ' + (countInCart * price).toFixed(2) + 'грн.');
$('#js-in_cart_' + product_id).animate({"height": "show"}, 500);
}
countInCart = 0;
},
});
}
//удаление товара из корзины с удалением из БД
$('.delete-order').on('click', function () {
var id = $($(this).closest('span')).data('id');
let parent = $(this).closest(".t812__pricelist-item");
let sum = parseFloat(parent.find(".sum").text());
let obj = $(this);
$.ajax({
url: '/dfb',
method: 'POST',
data: {'id': id},
headers: {'X-CSRF-TOKEN': getToken()},
success: function (data) {
if (data == 1) {
$("#" + id).animate({"height": "hide"}, 400);
changeTotalSum(obj, del = sum);
}
}
});
//если товар последний в корзине, то перезагрузим страницу
let cart = $(this).closest(".cart_js");
let prods = cart.children('.t812__pricelist-item');
var n = -1;
prods.each(function () {
if ($(this).css('display') != 'none') {
n++;
}
});
if (n === 0) {
setTimeout(window.location.reload.bind(window.location), 100);
}
return false;
});
//отображение картинки товара в swal по клику в корзине
$(".imgjs").on('click', function () {
let img = $(this).attr("src");
let alt = $(this).attr("alt");
swal({
text: alt,
className: "my_swal_ok",
icon: img,
imageHeight: 900,
imageAlt: alt,
// timer: 2000,
});
});