jQuery-плагин корзины интернет-магазина

в 10:36, , рубрики: javascript, jquery, интернет-магазин, метки: , ,

При разработке интернет-магазинов мне часто приходилось посредством javascript (а конкретнее его фреймворком — jQuery) проделывать некоторые повторяющиеся от проекта к проекту вещи. Например, добавление товаров в корзину, обновление информации о количестве товаров и общей сумме на странице, удаление товаров из корзины по одному, изменение их количества и полная очистка всех товаров из корзины. И все это, разумеется, без перезагрузки страницы.
И я пришел к выводу, что нужно писать код так, что бы потом его с легкостью можно было использовать в следующих проектах.

Итак пишем jQuery-плагин для работы с корзиной интернет-магазина.
Оговорюсь, что я опубликую только javascript-код, серверную часть трогать не буду, обозначу так же, какими данными будет обмениваться клиент с сервером.

Определяемся с методами:

(function($) {
	$.Cart = {
		init : function(settings){
			// инициализация плагина
		},
		add : function(context){
			// добавление одного наименования товара в корзину в количестве 1шт.
		},
		get : function(){
			// обновление информации о количестве товаров в корзине и общей сумме 
		},
		count : function(context) {
			// изменение количества товаров одного и того же наименования в корзине
		},
		del : function(context) {
			// удаление одного наименования из корзины
		},
		clear : function() {
			// полная очистка корзины
		},		
		showMessage : function(message) {
			// показ сообщения при добавлении товара
		}
	}
})(jQuery);

Инициализация:
Что нам нужно сделать в инициализации?
Необходимо переданные в метод init настройки, должным образом обработать (совместить с параметрами по умолчанию) и применить их, а так же повесить на элементы заданные в настройках соответствующие обработчики событий.
Определяемся с параметрами по умолчанию.

var defaults = {
	content : '.content', // селектор главного блока на странице корзины
	count : '.prod span font', // в блоке .prod на странице количество товаров в корзине
	summ : '.summ span font', // в блоке .prod общая сумма
	add : '.tocart', // селектор элемента, при нажатии на который товар будет добавлен в корзину
	addattr : 'name', // аттрибут элемента, содержащий ID товара
	counta : '.carts .actions .count input', // поле содержащее количество товаров одного наименования в корзине
	countattr : 'name', // аттрибут поля, содержащий ID товара
	countvattr : 'value', // аттрибут поля, содержащий количество товаров
	del : '.carts .actions .delete input', // селектор элемента, при нажатии на который 1 наименование товара будет удалено из корзины
	delattr : 'name', // аттрибут элемента, содержащий ID товара
	clear : '.cartclear', // селектор элемента, при нажатии на который корзина будет очищена
	topcart : '.topcart', // селектор ссылки ведущей в корзину
	urladd : '/cart/add/', // url добавления товара в корзину
	urlget : '/cart/get/', // url информации о количестве товаров в корзине и общей сумме
	urlcart : '/cart/', // url непосредственно самой корзины
	urlcount : '/cart/count/', // url обновления количества товаров одного наименования
	urlclear : '/cart/clear/', // url полной очистки корзины
	urldel : '/cart/del/', // url удаления одного наименования товара из корзины
	type_message : 'flash', // тип сообщения при добавлении товара в корзину
	time_message : 3000, // время показа сообщения
	interval : 10000 // интервал времени, через который будет обновляться блок с информацией о количестве товаров в корзине и общей сумме
};

Смешиваем параметры указанные при инициализации с параметрами по умолчанию и применяем их

$.extend(defaults, settings);
this.settings = defaults;

Делаем обновление блока с информацией о количестве товаров в корзине и общей сумме, вешаем обработчики событий на заданные элементы и возвращаем сущность объекта (может пригодится).

this.get(); // обновляем блок
setInterval(function(){
	$.Cart.get(); // обновляем блок через заданный в параметрах интервал
}, settings.interval);
$(settings.add).click(function(){
	$.Cart.add(this); // добавляем товар в корзину при клике на элемент settings.add, в качестве параметра передаем сам элемент
});
$(settings.counta).live('blur', function(){
	$.Cart.count(this); // при потере фокуса поля, содержащего количество товаров одного наименования в корзине, передаем информацию на сервер
});
$(settings.del).live('click', function(){
	$.Cart.del(this); // удаляем наименование товара при нажатии на элемент "удалить"
});
$(settings.clear).live('click', function(){
	$.Cart.clear(); // очищаем корзину
});
return this;

Добавление одного наименования товара в корзину в количестве 1шт.
Метод add, получает параметр context (элемент, на который произошел клик) и отправляет на сервер данные.

$.ajax({
	url : $.Cart.settings.urladd, // куда отправляем данные
	type : 'post', // метод
	dataType: 'json', // тип данных, которые получаем
	data : {id : $(context).attr($.Cart.settings.addattr)}, // данные, которые отправляем (id добавляемого товара)
	success : function(data){ // при получении ответа
	if (data.count && data.summ || data.count===0 && data.summ===0) { // проверяем есть ли в нем количество и общая сумма для того, что бы сразу
		$($.Cart.settings.count).html(data.count); // обновить количество товаров в блоке
		$($.Cart.settings.summ).html(data.summ); // обновить общую сумму в блоке
		if (data.count>0) // если товары есть в корзине, то
			$($.Cart.settings.topcart).find('a').attr('href', $.Cart.settings.urlcart); // делаем ссылку на корзину активной
		else // если корзина пуста
			$($.Cart.settings.topcart).find('a').attr('href', 'javascript:void(0);'); // делаем ссылку неактивной
		$.Cart.showMessage('<p>'+data.message+'</p>'+$($.Cart.settings.topcart).html()); // показываем сообщение
	}
});

Получение информации о количестве товаров в корзине и общей сумме, обновление блока (метод get).
Отличе метода get от add в url на который отправляются данные и в параметрах (метод get не передает параметров).

$.ajax({
	url : $.Cart.settings.urlget,
	type : 'post',
	dataType: 'json',
	data : {},
	success : function(data){
	if (data.count && data.summ || data.count===0 && data.summ===0) {
		$($.Cart.settings.count).html(data.count);
		$($.Cart.settings.summ).html(data.summ);
		if (data.count>0)
			$($.Cart.settings.topcart).find('a').attr('href', $.Cart.settings.urlcart);
		else
			$($.Cart.settings.topcart).find('a').attr('href', 'javascript:void(0);');
	}
});

Думаю, дабы осуществить повторное использование кода, можно добавить некоторый метод updateCart, в который будут переданы url и vars (параметры).

$.ajax({
	url : data.url, 
	type : 'post',
	dataType: 'json',
	data : data.vars,
	success : function(data){
	if (data.count && data.summ || data.count===0 && data.summ===0) {
		$($.Cart.settings.count).html(data.count);
		$($.Cart.settings.summ).html(data.summ);
		if (data.count>0)
			$($.Cart.settings.topcart).find('a').attr('href', $.Cart.settings.urlcart);
		else
			$($.Cart.settings.topcart).find('a').attr('href', 'javascript:void(0);');
		$.Cart.showMessage('<p>'+data.message+'</p>'+$($.Cart.settings.topcart).html());
	}
});

В свою очередь метод add изменится на такой.

$.Cart.updateCart({
	url : $.Cart.settings.urladd,
	data : {id : $(context).attr($.Cart.settings.addattr)}
});

Метод get.

$.Cart.updateCart({
	url : $.Cart.settings.urlget,
	data : {}
});

Изменение количества товаров одного наименования при потере фокуса поля, соответствующего этому наименованию.

count : function(context) { // в метод count передается параметр context (элемент - поле соответствующее наименованию)
	$.post($.Cart.settings.urlcount, { // отправляем запрос на соответствующий url и передаем
		id : $(context).attr($.Cart.settings.countattr), // ID наименования товара
		count : $(context).attr($.Cart.settings.countvattr) // количество товаров этого наименования
	}, function(data){ // получаем данные (text/html)
		$($.Cart.settings.content).html(data); // обновляем все содержимое корзины
		$.Cart.get(); // обновляем блок корзины с краткой информацией
	});
},

Удаление наименования.

del : function(context) { // в метод del передается параметр context (элемент - на который совершен клик для удаления наименования)
	$.post($.Cart.settings.urldel, {  // отправляем данные
		id : $(context).attr($.Cart.settings.delattr) // ID наименования
	}, function(data){ // получаем данные (text/html)
		$($.Cart.settings.content).html(data); // обновляем все содержимое корзины
		$.Cart.get(); // обновляем блок корзины с краткой информацией
	});
},

Очистка корзины.

clear : function() {
	$.post($.Cart.settings.urlclear, {}, function(data){
		$($.Cart.settings.content).html(data); // обычно в data приходит информация о том, что корзина пуста
		$.Cart.get(); // здесь будет всё по нулям, а ссылка на корзину станет неактивной
	});
},

Сообщение о добавлении товара в корзину (в зависимости от типа alert или flash).

showMessage : function(message) { // передается текст сообщения
	if ($.Cart.settings.type_message==='alert') { // если alert
		alert(message); // комментарии излишни
		return;
	} else if ($.Cart.settings.type_message==='flash') { // если flash
		if ($('.flashmessage').length===0) // если элементов с сообщением в документе не найдено, то
			$('<div />').addClass('flashmessage').html(message).hide().appendTo('body').fadeIn(); // создаем его, добавляем в него сообщение, вставляем его перед закрывающим тегом </body>, и плавно отображаем
		else // если найдено
			$('.flashmessage').html(message); // то вставляем в него сообщение
		if (typeof($.Cart.message_time)==='number') // если уже происходит отсчет времени до завершения показа сообщения
			clearTimeout($.Cart.message_time); // сбрасываем его
		$.Cart.message_time = setTimeout(function(){ // заводим новый таймер
			$('.flashmessage').fadeOut(function(){ // по истечению времени показа плавно скрываем
				$(this).remove(); // при завершении скрытия удаляем элемент
			});
		}, $.Cart.settings.time_message); // время показа сообщения
	}
}

Теперь это нужно все объединить.

/**
 * jQuery Cart Plugin 1.0
 *
 * Copyright (c) 2012 Shilov Vasily
 * Site: www.aiway.ru
 * E-mail: support@aiway.ru
 */
(function($) {
	var defaults = {
		content : '.content',
		count : '.prod span font',
		summ : '.summ span font',
		add : '.tocart',
		addattr : 'name',
		counta : '.carts .actions .count input',
		countattr : 'name',
		countvattr : 'value',
		del : '.carts .actions .delete input',
		delattr : 'name',
		clear : '.cartclear',
		topcart : '.topcart',
		urladd : '/cart/add/',
		urlget : '/cart/get/',
		urlcart : '/cart/',
		urlcount : '/cart/count/',
		urlclear : '/cart/clear/',
		urldel : '/cart/del/',
		type_message : 'flash',
		time_message : 3000,
		interval : 10000
	};
	$.Cart = {
		init : function(settings){
			$.extend(defaults, settings);
			this.settings = defaults;
			this.get();
			setInterval(function(){
				$.Cart.get();
			}, settings.interval);
			$(settings.add).click(function(){
				$.Cart.add(this);
			});
			$(settings.counta).live('blur', function(){
				$.Cart.count(this);
			});
			$(settings.del).live('click', function(){
				$.Cart.del(this);
			});
			$(settings.clear).live('click', function(){
				$.Cart.clear();
			});
			return this;
		},
		add : function(context){
			$.Cart.updateCart({
				url : $.Cart.settings.urladd,
				data : {id : $(context).attr($.Cart.settings.addattr)}
			});
		},
		get : function(){
			$.Cart.updateCart({
				url : $.Cart.settings.urlget,
				data : {}
			});
		},
		count : function(context) {
			$.post($.Cart.settings.urlcount, {
				id : $(context).attr($.Cart.settings.countattr),
				count : $(context).attr($.Cart.settings.countvattr)
			}, function(data){
				$($.Cart.settings.content).html(data);
				$.Cart.get();
			});
		},
		del : function(context) {
			$.post($.Cart.settings.urldel, {
				id : $(context).attr($.Cart.settings.delattr)
			}, function(data){
				$($.Cart.settings.content).html(data);
				$.Cart.get();
			});
		},
		clear : function(){
			$.post($.Cart.settings.urlclear, {}, function(data){
				$($.Cart.settings.content).html(data);
				$.Cart.get();
			});
		},
		updateCart : function(data){
			$.ajax({
				url : data.url, 
				type : 'post',
				dataType: 'json',
				data : data.vars,
				success : function(data){
				if (data.count && data.summ || data.count===0 && data.summ===0) {
					$($.Cart.settings.count).html(data.count);
					$($.Cart.settings.summ).html(data.summ);
					if (data.count>0)
						$($.Cart.settings.topcart).find('a').attr('href', $.Cart.settings.urlcart);
					else
						$($.Cart.settings.topcart).find('a').attr('href', 'javascript:void(0);');
					$.Cart.showMessage('<p>'+data.message+'</p>'+$($.Cart.settings.topcart).html());
				}
			});
		},
		showMessage : function(message){
			if ($.Cart.settings.type_message==='alert') {
				alert(message);
				return;
			} else if ($.Cart.settings.type_message==='flash') {
				if ($('.flashmessage').length===0)
					$('<div />').addClass('flashmessage').html(message).hide().appendTo('body').fadeIn();
				else
					$('.flashmessage').html(message);
				if (typeof($.Cart.message_time)==='number')
					clearTimeout($.Cart.message_time);
				$.Cart.message_time = setTimeout(function(){
					$('.flashmessage').fadeOut(function(){
						$(this).remove();
					});
				}, $.Cart.settings.time_message);
			}
		}
	}
})(jQuery);

Спасибо за внимание. Жду критики, вопросов и замечаний.
Есть желание доработать это до некого API работы с интернет-магазинами.

Автор: web36m

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js