Корзина покупателя на jQuery.

Плагин jqCart.

Когда писал предыдущую статейку на эту тему, которая преследовала цель всего лишь показать абстрактный пример реализации корзины покупателя, то не думал, что она породит такое количество вопросов и просьб, посыпавшихся и в самом блоге, и на мою почту, и т.д. Собственно, поэтому и решил написать этот плагинчик "jqCart", но хочу сразу подчеркнуть несколько моментов:

  1. Хоть код и оформлен в виде плагина, но плагином его можно назвать с большой натяжкой. Да и вообще, в этом направлении, на мой взгляд, сделать плагин полностью универсальным - достаточно сложно;
  2. Писа́лся плагин на скорую руку, поэтому достаточно сыроват, хотя и вполне рабочий;
  3. На данный момент, код не документированный;
  4. Планирую ли я его дорабатывать? Да, но при достаточном количестве свободного времени;

Итак, для работы плагина требуется библиотека jQuery >= 1.8, которая должна быть подключена до подключения самого плагина. Работать должно во всех современных браузера и, по идее, даже в IE8. Проблема для старых "осликов", может заключаться только в применяемых CSS-свойствах и версии jQuery (напомню, что jQuery 2.x - не поддерживает Internet Explorer 6, 7, и 8). Данные передаются на сервер с помощью Ajax и поэтому, я крайне рекомендую использовать кодировку для файлов UTF-8 без BOM!

Подключение:

<link href="css/jqcart.css" rel="stylesheet" type="text/css">
<script src="js/jquery-1.11.3.min.js"></script>
<script src="js/jqcart.min.js"></script>

Использование:

$(function() {
    'use strict';
    // инициализация плагина
    $.jqCart({
        buttons: '.add_item',        // селектор кнопок, аля "Добавить в корзину"
        handler: '/php/handler.php', // путь к обработчику
        visibleLabel: false,         // показывать/скрывать ярлык при пустой корзине (по умолчанию: false)
        openByAdding: false,         // автоматически открывать корзину при добавлении товара (по умолчанию: false)
        currency: '&euro;',          // валюта: строковое значение, мнемоники (по умолчанию "$")
        cartLabel: '.label-place'    /* селектор элемента, где будет размещен ярлык, 
                                        он же - "кнопка" для открытия корзины */
    });
    
    // дополнительные методы
    $.jqCart('openCart'); // открыть корзину
    $.jqCart('clearCart'); // очистить корзину
});

В кнопках ("Добавить в корзину"), должены быть прописаны следующие data-атрибуты:

  1. data-id - ID товара
  2. data-title - Наименование товара
  3. data-price - Цена товара
  4. data-img - URL фото товара (опционально)

Все значения вышеуказанных data-атрибутов, принимают участие при формировании окна корзины. Можно добавлять дополнительные data-атрибуты, значения которых будут отправлены с остальными данными в обработчик. Ключи в полученном массиве на сервере, будут соответствовать имени атрибута после "data-", т.е. значение атрибута "data-somevalue", будет в массиве под ключем "somevalue" Тег для кнопки и её расположение на странице - значения не имеет.

Пример:

<button class="add_item" data-id="7" data-title="Bugatti Veyron Super Sports" data-price="200" data-img="http://site.com/img/photo.png">Добавить в корзину</button>

В архиве найдете пример обработчика (handler.php) с подготовкой и отправкой письма на почту. В конце обработчика, обязательно должен быть ответ клиенту в формате JSON.

<?php
// какой-то код обработки заказа...

// Ответ на запрос
// !для версии PHP < 5.4, используйте традиционный синтаксис инициализации массивов array() вместо короткого []
$response = [
    'errors' => !$send_ok,
    'message' => $send_ok ? 'Заказ принят в обработку!' : 'Хьюстон! У нас проблемы!'
];
exit( json_encode($response) );

Где переменная $send_ok - булевое значение (true/false), в зависимости от результата обработки заказа. Если это будет отправка на почту, то можно так:

<?php
$send_ok = mail($to, $subject, $body, implode("\r\n", $headers));

Ахтунг! В обработчике я не делал фильтрацию данных, поэтому внимательно и тщательно их обрабатывайте перед использованием, а особенно, если вы собираетесь записывать данные в БД!

Версия 1.1.0

  • Данные распределены по разным data-атрибутам кнопки
  • Добавлена возможность отображения фото товара в корзине
  • Добавлен параметр currency для вывода в корзине валюты рядом со стоимостью и ценой
  • Добавлена возможность передавать на сервер дополнительные данные, которые берутся из пользовательских data-атрибутов кнопки

Версия 1.1.1

  • Добавлена опция visibleLabel. Показывать ярлык при пустой корзине
  • Добавлена опция openByAdding. Автоматически открывать корзину при добавлении товара
  • Добавлена кнопка вывода заказа на печать

Версия 1.1.2

  • Исправлен расчет сумм с плавающей точкой

Просьба описывать в комментариях найденные вами ошибки/баги. По мере возможностей, я буду их править, НО глобальными изменениями или дополнениями, буду заниматься, как уже говорил, при наличии свободного времени

Incode Pro logo

687 комментариев

Страница 3 из 28  
Гость 08.10.2018 11:42
Подскажите, как изменить минимальную сумму заказа в примере? Не пойму откуда берется параметр 2000. Спасибо.

вопрос снят, можно комент удалить.Спасибо за корзину.
Гость 10.10.2018 10:46
Как сделать чтобы при выводе на печать заполнялись формы введенные в корзине?
Гость 10.10.2018 23:41

Гость 10.10.2018 10:46
Как сделать чтобы при выводе на печать заполнялись формы введенные в корзине?


Показать код
<!doctype html>
<html>
<head>
<meta charset="utf-8">

<title>Корзина</title>
<link href="css/jqcart.css" rel="stylesheet" type="text/css">
<script src="js/jquery-1.11.3.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.2/css/font-awesome.min.css">
</head>
<body>
<script>
$(function () {
'use strict';
// инициализация плагина
$.jqCart({
handler: './php/handler.php', //!!!!!!!!!
currency: '&#8381;'
});
});
</script>
<script>
(function ($) {
'use strict';
var cartData,
itemData,
orderPreview = '',
totalCnt = 0,
visibleLabel = false,
orderform = '';
var opts = {
buttons: '.add_item',
cartLabel: 'body',
visibleLabel: false,
openByAdding: false,
handler: '/',
currency: ' р.'
};

var actions = {
init: function (o) {
opts = $.extend(opts, o);


$(document)
.on('click', '.jqcart-incr', actions.changeAmount)
.on('input keyup', '.jqcart-amount', actions.changeAmount)
.on('click', '.jqcart-del-item', actions.delFromCart)
.on('submit', '.jqcart-orderform', actions.sendOrder)
.on('reset', '.jqcart-orderform', actions.hideCart)
.on('click', '.jqcart-print-order', actions.printOrder)
.on('click', '.jqcart-clearCart-order', methods.clearCart);
return false;
},

delFromCart: function () {
var $that = $(this),
line = $that.closest('.jqcart-tr'),
itemId = line.data('id');
cartData = actions.getStorage();
actions.changeTotalCnt(-cartData[itemId].count);
delete cartData[itemId];
actions.setStorage(cartData);
line.remove();
actions.recalcSum();
return false;
},
changeAmount: function () {
var $that = $(this),
manually = $that.hasClass('jqcart-amount'),
amount = +(manually ? $that.val() : $that.data('incr')),
itemId = $that.closest('.jqcart-tr').data('id');
cartData = actions.getStorage();
if (manually) {
cartData[itemId].count = isNaN(amount) || amount < 1 ? 1 : amount;
} else {
cartData[itemId].count += amount;
}
if (cartData[itemId].count < 1) {
cartData[itemId].count = 1;
}
if (cartData[itemId].count > 12) {
cartData[itemId].count = 12;
}
if (manually) {
$that.val(cartData[itemId].count);
} else {
$that.siblings('input').val(cartData[itemId].count);
}
actions.setStorage(cartData);
actions.recalcSum();
return false;
},
recalcSum: function () {
var subtotal = 0,
amount,
sum = 0,
totalCnt = 0;
$('.jqcart-tr').each(function () {
amount = +$('.jqcart-amount', this).val();
sum = Math.ceil((amount * $('.jqcart-price', this).text()) * 100) / 100;
$('.jqcart-sum', this).html(sum + ' ' + opts.currency);
subtotal = Math.ceil((subtotal + sum) * 100) / 100;
totalCnt += amount;
});
$('.jqcart-subtotal strong').text(subtotal);
if (totalCnt <= 0) {
location.reload(); ///////////////////////actions.hideCart();

}
return false;
},
changeTotalCnt: function (n) {
var cntOutput = $('.jqcart-total-cnt');
cntOutput.text((+cntOutput.text() + n));
return false;
},
sendOrder: function (e) {
e.preventDefault();
var $that = $(this);
if ($.trim($('[name=user_name]', $that).val()) === '' || $.trim($('[name=user_phone]', $that).val()) === '') {
$('<p class="jqcart-error">Пожалуйста, укажите свое имя и контактный телефон!</p>').insertBefore($that).delay(3000).fadeOut();
return false;
}
$.ajax({
url: opts.handler,
type: 'POST',
dataType: 'json',
data: {
orderlist: $.param(actions.getStorage()),
userdata: $that.serialize()
},
error: function () {
},
success: function (resp) {
$('.jqcart-checkout').html('<p>' + resp.message + '</p>');
if (!resp.errors) {
setTimeout(methods.clearCart, 2000);
}
}
});
},
printOrder: function () {
var contents = $(this).closest('.jqcart-checkout').prop('outerHTML');
if (!contents) {
return false;
}
var d = new Date();
var curDate = ('0' + d.getDate()).slice(-2) + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + d.getFullYear() + ' ' + ('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2) + ':' + ('0' + d.getSeconds()).slice(-2);

var frame1 = $('<iframe />');
frame1[0].name = "frame1";
frame1.css({"position": "absolute", "top": "-1000000px"});
$("body").append(frame1);
var frameDoc = frame1[0].contentWindow ? frame1[0].contentWindow : frame1[0].contentDocument.document ? frame1[0].contentDocument.document : frame1[0].contentDocument;
frameDoc.document.open();
//Create a new HTML document.
frameDoc.document.write('<html><head><title>Заказ ' + curDate + '</title>');
frameDoc.document.write('</head><body>');
//Append the external CSS file.
frameDoc.document.write('<link href="css/jqcart.css" rel="stylesheet" type="text/css" />');
//Append the DIV contents.
frameDoc.document.write(contents);
frameDoc.document.write('</body></html>');
frameDoc.document.close();
setTimeout(function () {
window.frames["frame1"].focus();
window.frames["frame1"].print();
frame1.remove();
}, 500);
},

setStorage: function (o) {
localStorage.setItem('jqcart', JSON.stringify(o));
return false;
},
getStorage: function () {
return JSON.parse(localStorage.getItem('jqcart'));
}
};
var methods = {
clearCart: function () {
localStorage.removeItem('jqcart');
location.reload(); ///////////////////////actions.hideCart();
},
printOrder: actions.printOrder,
test: function () {
actions.getStorage();
}
};
$.jqCart = function (opts) {
if (methods[opts]) {
return methods[opts].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof opts === 'object' || !opts) {
return actions.init.apply(this, arguments);
} else {
$.error('Метод с именем "' + opts + '" не существует!');
}
};

var subtotal = 0,
cartHtml = '';
cartData = actions.getStorage();
orderPreview = '<div class="jqcart-checkout">';
orderPreview += '<p class="jqcart-cart-title">Товарный чек <span class="jqcart-print-order"></span></p><div class="jqcart-table-wrapper"><div class="jqcart-manage-order"><div class="jqcart-thead"><div><p>&nbsp;</p>&nbsp;Артикул&nbsp;<p>&nbsp;</p></div><div>Наименование товара</div><div>Количество</div><div>Цена</div><div>Сумма</div></div>';
var key, sum = 0;
for (key in cartData) {
if (cartData.hasOwnProperty(key)) {
sum = Math.ceil((cartData[key].count * cartData[key].price) * 100) / 100;
subtotal = Math.ceil((subtotal + sum) * 100) / 100;
orderPreview += '<div class="jqcart-tr" data-id="' + cartData[key].id + '">';
orderPreview += '<div align="center" class="jqcart-small-td">' + cartData[key].id + '</div>';
orderPreview += '<div>' + cartData[key].title + '</div>';
orderPreview += '<div align="center">' + cartData[key].count + '</div>';
orderPreview += '<div align="center" class="jqcart-price">' + cartData[key].price + '</div>';
orderPreview += '<div align="center" class="jqcart-sum">' + sum + ' ' + opts.currency + '</div>';
orderPreview += '</div>';
}
}
orderPreview += '</div></div>';
orderPreview += '<p><br>123456</p>';
orderPreview += '<div class="jqcart-subtotal">Итого: <strong>' + subtotal + '</strong>' + opts.currency + '</div>';
cartHtml = subtotal ? (orderPreview + orderform) : '<h2 class="jqcart-empty-cart">Корзина пуста</h2>';
console.log(cartData);
document.write(cartHtml);
})(jQuery);
</script>
</body>
</html>
Jul 14.10.2018 08:31
Здравствуйте, уважаемый админ. Спасибо большое за корзину! Все работает! Ваша корзина - единственное стоящее произведение из всего что я смогла найти в интернете за неделю! Спасибо!
Гость 12.11.2018 16:43
Спасибо, пригодилась, хорошая штука. Фильтрацию минимальную сделал в виде striptags и капчу прикрутил, пока полет нормальный)
Гость 13.11.2018 09:41
Здравствуйте. Корзина удачная.
Но есть вопрос: можно ли вместо названия передавать переменное значение? У меня стоит конфигуратор подбора товара по цвету, значение выводится в коде через id="color". Например так:
<span id="color">БЕЛЫЙ</span>
.

Тоже самое касается и цены, можно ли использовать динамическое значение.
Incode 13.11.2018 23:22
можно ли вместо названия передавать переменное значение?
Можно передавать любые данные. В описании я указал это:
Можно добавлять дополнительные data-атрибуты, значения которых будут отправлены с остальными данными в обработчик.
Гость 25.11.2018 17:09
Подскажите я хочу создать вариации товара, то есть к примеру при нажатии кнопки меняю data-price и data-id но товар добавляется тот же. Как сделать повторную инициализацию плагина? Чтобы он добавлял измененный товар. Спасибо.
Гость 02.12.2018 06:12
т.е. вам не нужно считать кол-во, а просто добавлять товары в таблицу?
Гость 24.12.2018 18:59
Здравствуйте! Подскажите, как сделать, чтоб при добавлении в корзину, где нибудь выводить сообщение, что товар добавлен.... и еще, как сделать отдельную кнопку в товаре, при нажатии которой товар добавлялся с одновременным открытием корзины.... Буду очень благодарен... Корзина супер!
Incode 24.12.2018 19:05
выводить сообщение, что товар добавлен
Этот момент я не предусмотрел, но сейчас нет времени корректировать.
с одновременным открытием корзины
Перечитайте внимательно описание и в частности обратите внимание на параметр openByAdding.
Гость 24.12.2018 19:34
Перечитайте внимательно описание и в частности обратите внимание на параметр openByAdding.

Тут я разобрался (менять значение true i false)... Просто хотел одну кнопку 'добавить' отдельно и вторую 'купить' ... Можно ли две кнопки или нужно кучу кода переписывать? В любом случае спасибо....
Incode 24.12.2018 21:26
Можно ли две кнопки
Можно. Просто инициализируете плагин дважды, указывая в первом случае свой селектор для кнопок "Добавить" + параметр openByAdding: false, а во втором случае (для кнопок "Купить") - их селектор + параметр openByAdding: true.
// для кнопок "Добавить"
$.jqCart({
    buttons: '.add_item', // класс кнопок "Добавить"
    openByAdding: false // Не открывать корзину автоматом
    /* другие параметры */
});
// для кнопок "Купить"
$.jqCart({
    buttons: '.buy_item', // класс кнопок "Купить"
    openByAdding: true // Открывать корзину автоматом
    /* другие параметры */
});
Гость 24.12.2018 22:40
инициировал два раза плагин, как написали вы,с двумя кнопками с такими классами, но обе кнопки выполняют одинаковое действие, не зависимо от того что написаны разные buttons и разные значения для openByAdding.... Какой $.jqCart последний то правило openByAdding и выполняют обе кнопки....

$(function(){
$.jqCart({ 
			buttons: '.add_item',
			handler: './php/handler.php',
			cartLabel: '.label-place',
			visibleLabel: true,
			openByAdding: false,
			currency: 'грн.'
	});	
$.jqCart({
    buttons: '.buy_item', 
    handler: './php/handler.php',
	cartLabel: '.label-place',
	visibleLabel: true,
    openByAdding: true, 
    currency: 'грн.' 
  });		
	// Пример с дополнительными методами
	   
	$('.open').click(function(){	    
		$.jqCart('openCart'); // открыть корзину
	});
	$('.clear').click(function(){
		$.jqCart('clearCart'); // очистить корзину
	});	
});
<button class="buy_item"  >Купить</button>
<button class="add_item"  >Добавить</button>
         
Incode 24.12.2018 23:52
обе кнопки выполняют одинаковое действие
Да, вы правы. Метод-то я сделал глобальным... Усталость даёт о себе знать.
В общем, можно поступить таким образом:
$('.buy_item').on('click', function(){
    $(this).siblings('.add_item').trigger('click');
    $.jqCart('openCart');
});
Плагин инициализируется нормально один раз на кнопке "Добавить", без автооткрытия корзины. При клике на кнопку "Купить" - триггер события на кнопку "Добавить" и открываем корзину $.jqCart('openCart');. В примере выше, я исходил из того, что обе кнопки - это сестринские элементы, поэтому использовал метод siblings().
Гость 25.12.2018 00:29
При клике на кнопку "Купить" - триггер события на кнопку "Добавить" и открываем корзину $.jqCart('openCart');.


Извините, что донимаю вас... но уж как будет время... вообщем пробовал и так и сяк... и кэш чистил... При нажатии на .buy_item молчит.... ничего не происходит...

$(function(){
    $.jqCart({ 
			buttons: '.add_item',
			handler: './php/handler.php',
			cartLabel: '.label-place',
			visibleLabel: true,
			openByAdding: false,
			currency: 'грн.'
	});
	
$('.buy_item').on('click', function(){
    
    $(this).siblings('.add_item').trigger('click');
    $.jqCart('openCart');
});
});
Incode 25.12.2018 00:43
ничего не происходит
Покажите часть html-кода, где у вас располагаются кнопки. Пример с двумя кнопками, описанный мной выше
Гость 25.12.2018 00:57
вот:


<ul id="tovar" >  
				<?php while( $row = mysqli_fetch_assoc($results) ){  ?>
		<li>	      
       <a id="title"><?= "<small>" . $row['brand'] . "</small><br/>".$row['model'] ?></a>     
       <img   style="position: relative;" src="/product/<?= $row['name']; ?>" >
       
       <div id="price" ><?= (int)$row['cena'].  " грн. "; ?></div>  
       <button class="buy_item"  data-id="<?= $row['id']; ?>" data-title="<?= $row['model']; ?>"   data-price="<?= $row['cena']; ?>" data-img="/product/<?= $row['name']; ?>">Купить</button>
       <button class="add_item"  data-id="<?= $row['id']; ?>" data-title="<?= $row['model']; ?>"   data-price="<?= $row['cena']; ?>" data-img="https://ensy.com.ua/product/<?= $row['name']; ?>">Добавить</button>
          
         </li>           		
				<?php }   ?>		
</ul>
Гость 25.12.2018 01:06
Ой... извините за лишние вопросы... оказывается, не работала кнопка buy_item из-за того что выборка товаров у меня загружается из другой страницы... сейчас перекинул js на ту страницу и все заработало!!!!! Огромнейшееее человеческое спасибо)))
Incode 25.12.2018 01:37
все заработало
Хорошо. Только data-атрибуты у кнопки "Купить" - лишние. Они всё равно никакой роли не играют, а утяжелять страницу мусором не нужно.
Гость 25.12.2018 12:40
Спасибо за ваше время!!!
Когда у вас будет время, подскажите как добавить всплывающее окно на 0.5 сек. что товар, добавлен, потому что когда в телефоне сидишь, не понятно был ли результат, после нажатия кнопки.....
И еще пытаюсь исправить, пока безуспешно .jqcart-checkout (модальное окно). Проблема в том, что прокрутки к таблице с товарами недостаточно, не влезает форма заказа в мобильной версии... А после добавления значения высоты, чтоб использовать скролл, увеличиваются и сообщения о пустой корзине и об отправлении товара.... Пока вижу выход из этой ситуации, переносом формы заказа на другое окно возможно.... Что думаете?
Гость 31.12.2018 11:45
подскажите как добавить всплывающее окно на 0.5 сек


Как то так https://jsfiddle.net/spoon100500/hwqL3y69/
Koblanjumabaev 01.01.2019 17:20
Ребят, подскажите как можно добавить промокод в корзину, типа в корзине инпут, в инпуте вводишь промокод и после этого с итогов суммы какое то число,например 20$
Буду благодарен за помощь )
Koblanjumabaev 01.01.2019 17:20
итогов суммы отбирается какое то число,например 20$
Гость 02.01.2019 07:46
Поищите в предыдущих постах, вроде был такой вопрос.
Страница 3 из 28  
Ваш комментарий:
X